Updated on 2026-08-14
This commit is contained in:
commit
e77eff8935
27 changed files with 567 additions and 333 deletions
2
.idea/codeStyles/Project.xml
generated
2
.idea/codeStyles/Project.xml
generated
|
|
@ -205,4 +205,4 @@
|
|||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
</component>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
|
@ -58,40 +59,25 @@
|
|||
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data
|
||||
android:pathPrefix="/ndef"
|
||||
android:host="www.tangem.com"
|
||||
android:scheme="http" />
|
||||
<data
|
||||
android:pathPrefix="/ndef"
|
||||
android:host="www.tangem.com"
|
||||
android:scheme="https" />
|
||||
<data
|
||||
android:pathPrefix="/ndef"
|
||||
android:host="tangem.com"
|
||||
android:scheme="http" />
|
||||
<data
|
||||
android:pathPrefix="/ndef"
|
||||
android:host="tangem.com"
|
||||
android:scheme="https" />
|
||||
<data
|
||||
android:pathPrefix="/ndef"
|
||||
android:host="app.tangem.com"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
<data android:scheme="http" />
|
||||
<data android:host="www.tangem.com" />
|
||||
<data android:pathPrefix="/ndef" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.TECH_DISCOVERED" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<data android:scheme="https" />
|
||||
<data android:host="www.tangem.com" />
|
||||
<data android:pathPrefix="/ndef" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.nfc.action.TECH_DISCOVERED"
|
||||
android:resource="@xml/nfc_tech_filter" />
|
||||
<data android:scheme="http" />
|
||||
<data android:host="tangem.com" />
|
||||
<data android:pathPrefix="/ndef" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.TAG_DISCOVERED" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="https" />
|
||||
<data android:host="tangem.com" />
|
||||
<data android:pathPrefix="/ndef" />
|
||||
|
||||
<data android:scheme="https" />
|
||||
<data android:host="app.tangem.com" />
|
||||
<data android:pathPrefix="/ndef" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
|
|
|
|||
|
|
@ -87,12 +87,54 @@ sealed class AnalyticsParam {
|
|||
}
|
||||
|
||||
sealed class TxSentFrom(val value: String) {
|
||||
object Send : TxSentFrom("Send")
|
||||
object Swap : TxSentFrom("Swap")
|
||||
data class Send(
|
||||
override val blockchain: String,
|
||||
override val token: String,
|
||||
override val feeType: FeeType,
|
||||
) : TxSentFrom("Send"), TxData
|
||||
|
||||
data class Swap(
|
||||
override val blockchain: String,
|
||||
override val token: String,
|
||||
override val feeType: FeeType,
|
||||
) : TxSentFrom("Swap"), TxData
|
||||
|
||||
data class Approve(
|
||||
override val blockchain: String,
|
||||
override val token: String,
|
||||
override val feeType: FeeType,
|
||||
val permissionType: String,
|
||||
) : TxSentFrom("Approve"), TxData
|
||||
|
||||
object WalletConnect : TxSentFrom("WalletConnect")
|
||||
object Sell : TxSentFrom("Sell")
|
||||
}
|
||||
|
||||
sealed interface TxData {
|
||||
val blockchain: String
|
||||
val token: String
|
||||
val feeType: FeeType
|
||||
}
|
||||
|
||||
sealed class FeeType(val value: String) {
|
||||
object Fixed : FeeType("Fixed")
|
||||
object Min : FeeType("Min")
|
||||
object Normal : FeeType("Normal")
|
||||
object Max : FeeType("Max")
|
||||
|
||||
companion object {
|
||||
fun fromString(feeType: String): FeeType {
|
||||
return when (feeType) {
|
||||
Min.value -> Min
|
||||
Normal.value -> Normal
|
||||
Max.value -> Max
|
||||
Fixed.value -> Fixed
|
||||
else -> Fixed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class WalletCreationType(val value: String) {
|
||||
object PrivateKey : WalletCreationType("Private key")
|
||||
object NewSeed : WalletCreationType("New seed")
|
||||
|
|
@ -100,9 +142,13 @@ sealed class AnalyticsParam {
|
|||
}
|
||||
|
||||
companion object Key {
|
||||
const val BLOCKCHAIN = "blockchain"
|
||||
const val TOKEN = "Token"
|
||||
const val SOURCE = "Source"
|
||||
const val BALANCE = "Balance"
|
||||
const val BATCH = "Batch"
|
||||
const val FEE_TYPE = "Fee Type"
|
||||
const val PERMISSION_TYPE = "Permission Type"
|
||||
const val PRODUCT_TYPE = "Product Type"
|
||||
const val FIRMWARE = "Firmware"
|
||||
const val CURRENCY = "Currency"
|
||||
|
|
|
|||
|
|
@ -51,7 +51,17 @@ sealed class Basic(
|
|||
|
||||
class TransactionSent(sentFrom: AnalyticsParam.TxSentFrom) : Basic(
|
||||
event = "Transaction sent",
|
||||
params = mapOf(AnalyticsParam.SOURCE to sentFrom.value),
|
||||
params = buildMap {
|
||||
this[AnalyticsParam.SOURCE] = sentFrom.value
|
||||
if (sentFrom is AnalyticsParam.TxData) {
|
||||
this[AnalyticsParam.BLOCKCHAIN] = sentFrom.blockchain
|
||||
this[AnalyticsParam.TOKEN] = sentFrom.token
|
||||
this[AnalyticsParam.FEE_TYPE] = sentFrom.feeType.value
|
||||
}
|
||||
if (sentFrom is AnalyticsParam.TxSentFrom.Approve) {
|
||||
this[AnalyticsParam.PERMISSION_TYPE] = sentFrom.permissionType
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
class ScanError(error: Throwable) : Basic(
|
||||
|
|
|
|||
|
|
@ -77,12 +77,6 @@ sealed class Token(
|
|||
class ButtonPaste : Send("Button - Paste")
|
||||
class ButtonQRCode : Send("Button - QR Code")
|
||||
class ButtonSwapCurrency : Send("Button - Swap Currency")
|
||||
|
||||
class TransactionSent(type: CurrencyType, error: Throwable? = null) : Send(
|
||||
event = "Transaction Sent",
|
||||
params = mapOf("Token" to type.value),
|
||||
error = error,
|
||||
)
|
||||
}
|
||||
|
||||
sealed class Topup(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.features.disclaimer.ui
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.View.OVER_SCROLL_NEVER
|
||||
import android.webkit.WebView
|
||||
import androidx.transition.TransitionInflater
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.core.ui.fragments.setStatusBarColor
|
||||
|
|
@ -27,21 +28,6 @@ class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubs
|
|||
private val binding: FragmentDisclaimerBinding by viewBinding(FragmentDisclaimerBinding::bind)
|
||||
private val webViewClient = DisclaimerWebViewClient()
|
||||
|
||||
override fun configureTransitions() {
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
when (store.state.disclaimerState.showedFromScreen) {
|
||||
AppScreen.Home -> {
|
||||
enterTransition = inflater.inflateTransition(android.R.transition.slide_bottom)
|
||||
exitTransition = inflater.inflateTransition(android.R.transition.slide_top)
|
||||
}
|
||||
AppScreen.Details -> {
|
||||
enterTransition = inflater.inflateTransition(android.R.transition.fade)
|
||||
exitTransition = inflater.inflateTransition(android.R.transition.fade)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
addBackPressHandler(handler = this)
|
||||
|
|
@ -86,11 +72,29 @@ class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubs
|
|||
super.onStop()
|
||||
}
|
||||
|
||||
override fun configureTransitions() {
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
when (store.state.disclaimerState.showedFromScreen) {
|
||||
AppScreen.Home -> {
|
||||
enterTransition = inflater.inflateTransition(android.R.transition.slide_bottom)
|
||||
exitTransition = inflater.inflateTransition(android.R.transition.slide_top)
|
||||
}
|
||||
AppScreen.Details -> {
|
||||
enterTransition = inflater.inflateTransition(android.R.transition.fade)
|
||||
exitTransition = inflater.inflateTransition(android.R.transition.fade)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(DisclaimerAction.OnBackPressed)
|
||||
}
|
||||
|
||||
override fun newState(state: DisclaimerState) = with(binding) {
|
||||
if (activity == null || view == null) return
|
||||
|
||||
updateUiVisibility(state.disclaimer, state.progressState)
|
||||
binding.webView.loadUrl(state.disclaimer.getUri().toString())
|
||||
}
|
||||
|
||||
private fun updateUiVisibility(disclaimer: Disclaimer, progressState: ProgressState?) = with(binding) {
|
||||
|
|
@ -111,16 +115,19 @@ class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubs
|
|||
}
|
||||
ProgressState.Error -> {
|
||||
root.beginDelayedTransition()
|
||||
webView.hide()
|
||||
groupAccept.hide()
|
||||
webView.show()
|
||||
groupAccept.show()
|
||||
groupLoading.hide()
|
||||
groupError.show()
|
||||
groupError.hide()
|
||||
webView.loadLocalTermsOfServices()
|
||||
}
|
||||
else -> {
|
||||
webView.loadUrl(disclaimer.getUri().toString())
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(DisclaimerAction.OnBackPressed)
|
||||
private fun WebView.loadLocalTermsOfServices() {
|
||||
loadData(localTermsOfServices, "text/html", "UTF-8")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package com.tangem.tap.features.disclaimer.ui
|
||||
|
||||
internal val localTermsOfServices = """
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Legal Disclaimer</title>
|
||||
<style>
|
||||
@font-face{font-family:"SF Pro Display";src:url(/fonts/sf-pro/SFProDisplay-Light.eot) format("embedded-opentype"),url(/fonts/sf-pro/SFProDisplay-Light.woff2) format("woff2"),url(/fonts/sf-pro/SFProDisplay-Light.woff) format("woff"),url(/fonts/sf-pro/SFProDisplay-Light.ttf) format("truetype");font-weight:300;font-style:normal;font-display:swap}@font-face{font-family:"SF Pro Display";src:url(/fonts/sf-pro/SFProDisplay-Regular.eot) format("embedded-opentype"),url(/fonts/sf-pro/SFProDisplay-Regular.woff2) format("woff2"),url(/fonts/sf-pro/SFProDisplay-Regular.woff) format("woff"),url(/fonts/sf-pro/SFProDisplay-Regular.ttf) format("truetype");font-weight:400;font-style:normal;font-display:swap}@font-face{font-family:"SF Pro Display";src:url(/fonts/sf-pro/SFProDisplay-Medium.eot) format("embedded-opentype"),url(/fonts/sf-pro/SFProDisplay-Medium.woff2) format("woff2"),url(/fonts/sf-pro/SFProDisplay-Medium.woff) format("woff"),url(/fonts/sf-pro/SFProDisplay-Medium.ttf) format("truetype");font-weight:500;font-style:normal;font-display:swap}@font-face{font-family:"SF Pro Display";src:url(/fonts/sf-pro/SFProDisplay-Semibold.eot) format("embedded-opentype"),url(/fonts/sf-pro/SFProDisplay-Semibold.woff2) format("woff2"),url(/fonts/sf-pro/SFProDisplay-Semibold.woff) format("woff"),url(/fonts/sf-pro/SFProDisplay-Semibold.ttf) format("truetype");font-weight:600;font-style:normal;font-display:swap}@font-face{font-family:"SF Pro Display";src:url(/fonts/sf-pro/SFProDisplay-Bold.eot) format("embedded-opentype"),url(/fonts/sf-pro/SFProDisplay-Bold.woff2) format("woff2"),url(/fonts/sf-pro/SFProDisplay-Bold.woff) format("woff"),url(/fonts/sf-pro/SFProDisplay-Bold.ttf) format("truetype");font-weight:700;font-style:normal;font-display:swap}*{margin:0;padding:0}body{padding:0;margin:1rem;color:rgb(0 0 0);font-family:'SF Pro Display',-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}h1{font-weight:700;font-size:30px;line-height:1.2;margin-bottom:1rem}p{font-size:16px;line-height:20px;letter-spacing:-.24px;margin-bottom:1rem}
|
||||
</style>
|
||||
</head>
|
||||
<body><h1>Legal Disclaimer</h1>
|
||||
<p> 1. Tangem application (Software)</p>
|
||||
<p> The Software is intended for usage only with Tangem hardware wallets (Cards) via NFC interface. The
|
||||
Software DOES NOT:</p>
|
||||
<p> a) Generate, store, transmit, or have access to private (secret) cryptographic keys to blockchain
|
||||
wallets holding digital assets, including crypto-currency.</p>
|
||||
<p> b) Generate, store, transmit, or have access to secret keys, passwords, passphrases, recovery phrases
|
||||
that can be used to restore or to copy private (secret) keys to blockchain wallets holding digital assets, including
|
||||
crypto-currency.</p>
|
||||
<p> c) Provide exchange, trading, investment services on behalf of Tangem AG.</p>
|
||||
<p> 2. Risks related to the use of Software</p>
|
||||
<p> Tangem will not be responsible for any losses, damages or claims arising from events falling within the
|
||||
scope of the following five categories:</p>
|
||||
<p> a) Mistakes made by the user of any cryptocurrency-related software or service, e.g., forgotten
|
||||
passwords, payments sent to wrong addresses, and accidental deletion of blockchain wallets on Cards.</p>
|
||||
<p> b) Problems of Software and/or any blockchain- or cryptocurrency- related software or service, e.g.,
|
||||
corrupted files, incorrectly constructed transactions, unsafe cryptographic libraries, malware.</p>
|
||||
<p> c) Technical failures in the hardware of the user, including Cards, of any cryptocurrency-related
|
||||
software or service, e.g., data loss due to a faulty or damaged storage device.</p>
|
||||
<p> d) Security problems experienced by the user of any cryptocurrency-related software or service, e.g.,
|
||||
unauthorized access to users' wallets and/or accounts.</p>
|
||||
<p> e) Actions or inactions of third parties and/or events experienced by third parties, e.g., bankruptcy
|
||||
of service providers, information security attacks on service providers, and fraud conducted by third parties.</p>
|
||||
<p> 3. Trading and Investment risks</p>
|
||||
<p> There is considerable exposure to risk in any crypto-currency or other digital asset exchange
|
||||
transaction. Any transaction involving currencies involves risks including, but not limited to, the potential for
|
||||
changing economic conditions that may substantially affect the price or liquidity of a currency. Investments in
|
||||
crypto-currency exchange speculation may also be susceptible to sharp rises and falls as the relevant market values
|
||||
fluctuate. It is for this reason that when speculating in such markets it is advisable to use only risk capital.</p>
|
||||
<p> 4. Electronic Trading Risks</p>
|
||||
<p> Before you engage in transactions using an electronic system, you should carefully review the rules and
|
||||
regulations of the exchanges offering the system and/or listing the instruments you intend to trade. Online trading
|
||||
has inherent risk due to system response and access times that may vary due to market conditions, system
|
||||
performance, and other factors. You should understand these and additional risks before trading.</p>
|
||||
<p> 5. Compliance with tax obligations </p>
|
||||
<p> The users of the Software are solely responsible to determinate what, if any, taxes apply to their
|
||||
crypto-currency transactions. The owners of, or contributors to, the Software are NOT responsible for determining
|
||||
the taxes that apply to crypto-currency transactions. </p>
|
||||
<p> 6. No warranties </p>
|
||||
<p> The Software is provided on an "as is" basis without any warranties of any kind regarding the
|
||||
Software and/or any content, data, materials and/or services provided on the Software.
|
||||
</p>
|
||||
<p> 7. Limitation of liability</p>
|
||||
<p> Unless otherwise required by law, in no event shall the owners of, or contributors to, the Software be
|
||||
liable for any damages of any kind, including, but not limited to, loss of use, loss of profits, or loss of data
|
||||
arising out of or in any way connected with the use of the Software. In no way are the owners of, or contributors
|
||||
to, the Software responsible for the actions, decisions, or other behavior taken or not taken by you in reliance
|
||||
upon the Software.</p>
|
||||
<p> 8. Last amendment</p>
|
||||
<p> This disclaimer was amended for the last time on October 1st, 2020.</p></body>
|
||||
</html>
|
||||
""".trimIndent()
|
||||
|
|
@ -7,28 +7,19 @@ import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
|
|||
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.ton.TonTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.blockchain.common.TransactionError
|
||||
import com.tangem.blockchain.common.TransactionSender
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -39,25 +30,11 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
|||
import com.tangem.tap.domain.extensions.minimalAmount
|
||||
import com.tangem.tap.features.demo.DemoTransactionSender
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction
|
||||
import com.tangem.tap.features.send.redux.AmountAction
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi
|
||||
import com.tangem.tap.features.send.redux.*
|
||||
import com.tangem.tap.features.send.redux.FeeAction.RequestFee
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.features.send.redux.SendActionUi
|
||||
import com.tangem.tap.features.send.redux.states.ButtonState
|
||||
import com.tangem.tap.features.send.redux.states.ExternalTransactionData
|
||||
import com.tangem.tap.features.send.redux.states.MainCurrencyType
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
|
||||
import com.tangem.tap.features.send.redux.states.*
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdk
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.tap.walletCurrenciesManager
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -65,7 +42,7 @@ import kotlinx.coroutines.launch
|
|||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import java.util.EnumSet
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -134,9 +111,16 @@ private fun verifyAndSendTransaction(
|
|||
},
|
||||
sendAllCallback = {
|
||||
sendTransaction(
|
||||
action, walletManager, amountToSend, feeAmount, destinationAddress,
|
||||
sendState.transactionExtrasState, card, sendState.externalTransactionData,
|
||||
dispatch,
|
||||
action = action,
|
||||
walletManager = walletManager,
|
||||
amountToSend = amountToSend,
|
||||
feeAmount = feeAmount,
|
||||
feeType = sendState.feeState.selectedFeeType,
|
||||
destinationAddress = destinationAddress,
|
||||
transactionExtras = sendState.transactionExtrasState,
|
||||
card = card,
|
||||
externalTransactionData = sendState.externalTransactionData,
|
||||
dispatch = dispatch,
|
||||
)
|
||||
},
|
||||
reduceAmount,
|
||||
|
|
@ -145,8 +129,16 @@ private fun verifyAndSendTransaction(
|
|||
}
|
||||
else -> {
|
||||
sendTransaction(
|
||||
action, walletManager, amountToSend, feeAmount, destinationAddress,
|
||||
sendState.transactionExtrasState, card, sendState.externalTransactionData, dispatch,
|
||||
action = action,
|
||||
walletManager = walletManager,
|
||||
amountToSend = amountToSend,
|
||||
feeAmount = feeAmount,
|
||||
feeType = sendState.feeState.selectedFeeType,
|
||||
destinationAddress = destinationAddress,
|
||||
transactionExtras = sendState.transactionExtrasState,
|
||||
card = card,
|
||||
externalTransactionData = sendState.externalTransactionData,
|
||||
dispatch = dispatch,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -158,6 +150,7 @@ private fun sendTransaction(
|
|||
walletManager: WalletManager,
|
||||
amountToSend: Amount,
|
||||
feeAmount: Amount,
|
||||
feeType: FeeType,
|
||||
destinationAddress: String,
|
||||
transactionExtras: TransactionExtrasState,
|
||||
card: CardDTO,
|
||||
|
|
@ -243,17 +236,23 @@ private fun sendTransaction(
|
|||
dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED))
|
||||
tangemSdk.config.linkedTerminal = linkedTerminalState
|
||||
|
||||
val currencyType = AnalyticsParam.CurrencyType.Amount(amountToSend)
|
||||
when (sendResult) {
|
||||
is SimpleResult.Success -> {
|
||||
Analytics.send(Token.Send.TransactionSent(currencyType))
|
||||
dispatch(SendAction.SendSuccess)
|
||||
|
||||
if (externalTransactionData != null) {
|
||||
Analytics.send(Basic.TransactionSent(AnalyticsParam.TxSentFrom.Sell))
|
||||
dispatch(WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId))
|
||||
} else {
|
||||
Analytics.send(Basic.TransactionSent(AnalyticsParam.TxSentFrom.Send))
|
||||
Analytics.send(
|
||||
Basic.TransactionSent(
|
||||
sentFrom = AnalyticsParam.TxSentFrom.Send(
|
||||
blockchain = walletManager.wallet.blockchain.fullName,
|
||||
token = amountToSend.currencySymbol,
|
||||
feeType = feeType.convertToAnalyticsFeeType(),
|
||||
),
|
||||
),
|
||||
)
|
||||
dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
scope.launch(Dispatchers.IO) {
|
||||
|
|
@ -271,8 +270,6 @@ private fun sendTransaction(
|
|||
)
|
||||
val error = sendResult.error as? BlockchainSdkError ?: return@withMainContext
|
||||
|
||||
Analytics.send(Token.Send.TransactionSent(currencyType, error))
|
||||
|
||||
when (error) {
|
||||
is BlockchainSdkError.WrappedTangemError -> {
|
||||
val tangemSdkError = error.tangemError as? TangemSdkError ?: return@withMainContext
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.send.redux.states
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -8,7 +9,16 @@ import java.math.BigDecimal
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
enum class FeeType {
|
||||
SINGLE, LOW, NORMAL, PRIORITY
|
||||
SINGLE, LOW, NORMAL, PRIORITY;
|
||||
}
|
||||
|
||||
fun FeeType.convertToAnalyticsFeeType(): AnalyticsParam.FeeType {
|
||||
return when (this) {
|
||||
FeeType.SINGLE -> AnalyticsParam.FeeType.Fixed
|
||||
FeeType.LOW -> AnalyticsParam.FeeType.Min
|
||||
FeeType.NORMAL -> AnalyticsParam.FeeType.Normal
|
||||
FeeType.PRIORITY -> AnalyticsParam.FeeType.Max
|
||||
}
|
||||
}
|
||||
|
||||
data class FeeState(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -21,6 +22,9 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.tap.features.tokens.impl.presentation.states.NetworkItemState
|
||||
import kotlinx.collections.immutable.ImmutableCollection
|
||||
|
||||
/** This const configures how many items show to user and hide more than*/
|
||||
private const val MAX_VISIBLE_BRIEF_ICONS = 9
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
|
@ -37,10 +41,25 @@ internal fun BriefNetworksList(
|
|||
exit = fadeOut(),
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) {
|
||||
networks.forEach { network ->
|
||||
key(network.name + network.protocolName) {
|
||||
BriefNetworkItem(model = network)
|
||||
val iterator = networks.iterator()
|
||||
var index = 0
|
||||
while (iterator.hasNext()) {
|
||||
val network = iterator.next()
|
||||
if (index < MAX_VISIBLE_BRIEF_ICONS) {
|
||||
key(network.name + network.protocolName) {
|
||||
BriefNetworkItem(model = network)
|
||||
}
|
||||
} else {
|
||||
if (networks.size < MAX_VISIBLE_BRIEF_ICONS + 1) {
|
||||
key(network.name + network.protocolName) {
|
||||
BriefNetworkItem(model = network)
|
||||
}
|
||||
} else {
|
||||
HasMoreItem(moreCount = networks.size - index)
|
||||
break
|
||||
}
|
||||
}
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -50,8 +69,8 @@ internal fun BriefNetworksList(
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
internal fun BriefNetworkItem(model: NetworkItemState) {
|
||||
Box(modifier = Modifier.size(size = TangemTheme.dimens.size20)) {
|
||||
internal fun BriefNetworkItem(model: NetworkItemState, modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier.size(size = TangemTheme.dimens.size20)) {
|
||||
Image(
|
||||
painter = painterResource(id = model.iconResId.value),
|
||||
contentDescription = null,
|
||||
|
|
@ -76,4 +95,21 @@ internal fun BriefNetworkItem(model: NetworkItemState) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun HasMoreItem(moreCount: Int) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(size = TangemTheme.dimens.size20)
|
||||
.clip(CircleShape)
|
||||
.background(TangemTheme.colors.control.unchecked),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = "+$moreCount",
|
||||
style = TangemTheme.typography.overline,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -110,9 +110,10 @@ internal class WelcomeMiddleware {
|
|||
}
|
||||
|
||||
private fun handleInitialIntent(intent: Intent?) {
|
||||
val isBackgroundScanWasHandled = intentHandler.handleBackgroundScan(intent, hasSavedUserWallets = true)
|
||||
val isBackgroundScanNotHandled = !intentHandler.handleBackgroundScan(intent, hasSavedUserWallets = true)
|
||||
val hasNotIncompletedBackup = !backupService.hasIncompletedBackup
|
||||
|
||||
if (!isBackgroundScanWasHandled) {
|
||||
if (isBackgroundScanNotHandled && hasNotIncompletedBackup) {
|
||||
store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,14 +34,11 @@ class TransactionManagerImpl(
|
|||
) : TransactionManager {
|
||||
|
||||
override suspend fun sendApproveTransaction(
|
||||
networkId: String,
|
||||
feeAmount: BigDecimal,
|
||||
gasLimit: Int,
|
||||
destinationAddress: String,
|
||||
dataToSign: String,
|
||||
txData: ApproveTxData,
|
||||
derivationPath: String?,
|
||||
analyticsData: AnalyticsData,
|
||||
): SendTxResult {
|
||||
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
|
||||
val blockchain = requireNotNull(Blockchain.fromNetworkId(txData.networkId)) { "blockchain not found" }
|
||||
val walletManager = getActualWalletManager(blockchain, derivationPath)
|
||||
walletManager.update()
|
||||
val amount = Amount(value = BigDecimal.ZERO, blockchain = blockchain)
|
||||
|
|
@ -49,40 +46,55 @@ class TransactionManagerImpl(
|
|||
walletManager = walletManager,
|
||||
amount = amount,
|
||||
blockchain = blockchain,
|
||||
feeAmount = feeAmount,
|
||||
gasLimit = gasLimit,
|
||||
destinationAddress = destinationAddress,
|
||||
dataToSign = dataToSign,
|
||||
feeAmount = txData.feeAmount,
|
||||
gasLimit = txData.gasLimit,
|
||||
destinationAddress = txData.destinationAddress,
|
||||
dataToSign = txData.dataToSign,
|
||||
successEvent = AnalyticsParam.TxSentFrom.Approve(
|
||||
blockchain = blockchain.fullName,
|
||||
token = analyticsData.tokenSymbol,
|
||||
feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType),
|
||||
permissionType = analyticsData.permissionType ?: "",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendTransaction(
|
||||
networkId: String,
|
||||
amountToSend: BigDecimal,
|
||||
feeAmount: BigDecimal,
|
||||
gasLimit: Int,
|
||||
destinationAddress: String,
|
||||
dataToSign: String,
|
||||
txData: SwapTxData,
|
||||
isSwap: Boolean,
|
||||
currencyToSend: Currency,
|
||||
derivationPath: String?,
|
||||
analyticsData: AnalyticsData,
|
||||
): SendTxResult {
|
||||
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
|
||||
val blockchain = requireNotNull(Blockchain.fromNetworkId(txData.networkId)) { "blockchain not found" }
|
||||
val walletManager = getActualWalletManager(blockchain, derivationPath)
|
||||
walletManager.update()
|
||||
val amount = if (isSwap) {
|
||||
createAmountForSwap(amountToSend, currencyToSend, blockchain)
|
||||
createAmountForSwap(txData.amountToSend, txData.currencyToSend, blockchain)
|
||||
} else {
|
||||
createAmount(amountToSend, currencyToSend, blockchain)
|
||||
createAmount(txData.amountToSend, txData.currencyToSend, blockchain)
|
||||
}
|
||||
val successEvent = if (isSwap) {
|
||||
AnalyticsParam.TxSentFrom.Swap(
|
||||
blockchain = blockchain.fullName,
|
||||
token = analyticsData.tokenSymbol,
|
||||
feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType),
|
||||
)
|
||||
} else {
|
||||
AnalyticsParam.TxSentFrom.Send(
|
||||
blockchain = blockchain.fullName,
|
||||
token = analyticsData.tokenSymbol,
|
||||
feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType),
|
||||
)
|
||||
}
|
||||
return sendTransactionInternal(
|
||||
walletManager = walletManager,
|
||||
amount = amount,
|
||||
blockchain = blockchain,
|
||||
feeAmount = feeAmount,
|
||||
gasLimit = gasLimit,
|
||||
destinationAddress = destinationAddress,
|
||||
dataToSign = dataToSign,
|
||||
feeAmount = txData.feeAmount,
|
||||
gasLimit = txData.gasLimit,
|
||||
destinationAddress = txData.destinationAddress,
|
||||
dataToSign = txData.dataToSign,
|
||||
successEvent = successEvent,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +107,7 @@ class TransactionManagerImpl(
|
|||
gasLimit: Int,
|
||||
destinationAddress: String,
|
||||
dataToSign: String,
|
||||
successEvent: AnalyticsParam.TxSentFrom,
|
||||
): SendTxResult {
|
||||
val txData = walletManager.createTransaction(
|
||||
amount = amount,
|
||||
|
|
@ -110,7 +123,10 @@ class TransactionManagerImpl(
|
|||
FirebaseCrashlytics.getInstance().recordException(ex)
|
||||
return SendTxResult.UnknownError(ex)
|
||||
}
|
||||
return handleSendResult(sendResult)
|
||||
return handleSendResult(
|
||||
result = sendResult,
|
||||
successEvent = successEvent,
|
||||
)
|
||||
}
|
||||
|
||||
override fun getExplorerTransactionLink(networkId: String, txAddress: String): String {
|
||||
|
|
@ -319,10 +335,10 @@ class TransactionManagerImpl(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleSendResult(result: SimpleResult): SendTxResult {
|
||||
private fun handleSendResult(result: SimpleResult, successEvent: AnalyticsParam.TxSentFrom): SendTxResult {
|
||||
when (result) {
|
||||
is SimpleResult.Success -> {
|
||||
analytics.send(Basic.TransactionSent(AnalyticsParam.TxSentFrom.Swap))
|
||||
analytics.send(Basic.TransactionSent(sentFrom = successEvent))
|
||||
return SendTxResult.Success
|
||||
}
|
||||
is SimpleResult.Failure -> {
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@
|
|||
android:paddingStart="3dp"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingEnd="3dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintBottom_toTopOf="@id/ll_total"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:selectionRequired="true"
|
||||
|
|
@ -156,162 +156,125 @@
|
|||
app:chipMinTouchTargetSize="0dp" />
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
<View
|
||||
android:id="@+id/v_shipping_discount_background"
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_total"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="93dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@drawable/shape_rectangle_rounded_4"
|
||||
app:layout_constraintBottom_toTopOf="@id/v_total_background" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_delivery"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:src="@drawable/ic_shipping"
|
||||
app:layout_constraintBottom_toTopOf="@id/v_divider"
|
||||
app:layout_constraintStart_toStartOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintTop_toTopOf="@id/v_shipping_discount_background" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_delivery_worldwide"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="6dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:text="@string/shop_shipping"
|
||||
android:textColor="@color/textBlack"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/v_divider"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_delivery"
|
||||
app:layout_constraintTop_toTopOf="@id/v_shipping_discount_background" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_delivery_cost"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/shop_free"
|
||||
android:textColor="@color/textBlack"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/v_divider"
|
||||
app:layout_constraintEnd_toEndOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintTop_toTopOf="@id/v_shipping_discount_background" />
|
||||
|
||||
<View
|
||||
android:id="@+id/v_divider"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0.5dp"
|
||||
android:background="#BEBEBE"
|
||||
app:layout_constraintBottom_toBottomOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintStart_toStartOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintTop_toTopOf="@id/v_shipping_discount_background" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_promo_code"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:src="@drawable/ic_promo_code"
|
||||
app:layout_constraintBottom_toBottomOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintStart_toStartOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_divider" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_promo_code"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:hint="@string/shop_i_have_a_promo_code"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="textCapCharacters"
|
||||
android:paddingStart="6dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/textBlack"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_promo_code"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_divider" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pb_promo_code"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="28dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:indeterminateTint="@color/darkGray2"
|
||||
app:layout_constraintBottom_toBottomOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/v_shipping_discount_background"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_divider" />
|
||||
|
||||
|
||||
<View
|
||||
android:id="@+id/v_total_background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="21dp"
|
||||
android:background="@drawable/shape_rectangle_rounded_4"
|
||||
app:layout_constraintBottom_toTopOf="@id/ll_buttons" />
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@id/ll_buttons">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_total_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/shop_total"
|
||||
android:textColor="@color/textBlack"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/v_total_background"
|
||||
app:layout_constraintStart_toStartOf="@id/v_total_background"
|
||||
app:layout_constraintTop_toTopOf="@id/v_total_background" />
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/shape_rectangle_rounded_4">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_total_before_discount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="@drawable/shape_line"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/v_total_background"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_total"
|
||||
app:layout_constraintTop_toTopOf="@id/v_total_background"
|
||||
tools:text="$44.20" />
|
||||
<EditText
|
||||
android:id="@+id/et_promo_code"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:hint="@string/shop_i_have_a_promo_code"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="textCapCharacters"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/textBlack"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_total"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="6dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/v_total_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/v_total_background"
|
||||
app:layout_constraintTop_toTopOf="@id/v_total_background"
|
||||
tools:text="$44.20" />
|
||||
<ProgressBar
|
||||
android:id="@+id/pb_promo_code"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="28dp"
|
||||
android:indeterminateTint="@color/darkGray2"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pb_price"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:indeterminateTint="@color/darkGray2"
|
||||
app:layout_constraintBottom_toBottomOf="@id/v_total_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/v_total_background"
|
||||
app:layout_constraintTop_toTopOf="@id/v_total_background" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/v_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:background="#BEBEBE" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/shape_rectangle_rounded_4">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_total_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/shop_total"
|
||||
android:textColor="@color/textBlack"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_total_before_discount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="@drawable/shape_line"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_total"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="$44.20" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_total"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="6dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="$44.20" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pb_price"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:indeterminateTint="@color/darkGray2"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sold_out_desc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="14dp"
|
||||
android:background="@drawable/shape_rectangle_rounded_4"
|
||||
android:padding="16dp"
|
||||
android:text="@string/shop_sold_out_description_prefix"
|
||||
android:textColor="@color/text_tertiary"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_buttons"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ dependencies {
|
|||
|
||||
/** Coroutines */
|
||||
implementation(deps.kotlin.coroutines)
|
||||
implementation(deps.kotlin.coroutines.reactive)
|
||||
implementation(deps.kotlin.coroutines.rx2)
|
||||
|
||||
/** Logging */
|
||||
implementation(deps.timber)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.datasource.connection
|
||||
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||
import io.reactivex.BackpressureStrategy
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
|
|
@ -11,6 +13,8 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.reactive.asFlow
|
||||
|
||||
private const val PING_INTERVAL = 5_000
|
||||
|
||||
internal class RealInternetConnectionManager : NetworkConnectionManager {
|
||||
|
||||
private val scope =
|
||||
|
|
@ -18,11 +22,23 @@ internal class RealInternetConnectionManager : NetworkConnectionManager {
|
|||
SupervisorJob() + Dispatchers.IO + FeatureCoroutineExceptionHandler.create("RealInternetConnectionManager"),
|
||||
)
|
||||
|
||||
private val initialNetworkResult: Boolean by lazy {
|
||||
ReactiveNetwork
|
||||
.checkInternetConnectivity()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.blockingGet()
|
||||
}
|
||||
|
||||
override val isOnlineFlow: StateFlow<Boolean> = ReactiveNetwork
|
||||
.observeInternetConnectivity()
|
||||
.observeInternetConnectivity(
|
||||
InternetObservingSettings.builder()
|
||||
.interval(PING_INTERVAL)
|
||||
.build(),
|
||||
)
|
||||
.toFlowable(BackpressureStrategy.LATEST)
|
||||
.asFlow()
|
||||
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
|
||||
.stateIn(scope, SharingStarted.Lazily, initialValue = initialNetworkResult)
|
||||
|
||||
override val isOnline: Boolean
|
||||
get() = isOnlineFlow.value
|
||||
|
|
|
|||
|
|
@ -333,11 +333,10 @@
|
|||
<string name="send_transaction_success">Транзакция успешно подписана и отправлена в блокчейн. Баланс будет обновлен через некоторое время</string>
|
||||
<string name="send_validation_invalid_address">Неверный адрес</string>
|
||||
<string name="shop_buy_now">Купить сейчас</string>
|
||||
<string name="shop_free">Бесплатно</string>
|
||||
<string name="shop_i_have_a_promo_code">У меня есть промо-код…</string>
|
||||
<string name="shop_one_wallet">Tangem Wallet</string>
|
||||
<string name="shop_other_payment_methods">Другие способы оплаты</string>
|
||||
<string name="shop_shipping">Доставка</string>
|
||||
<string name="shop_sold_out_description_prefix">Из-за большого количества заказов, которые мы получаем, cроки доставки могут быть увеличены.</string>
|
||||
<string name="shop_total">Итого</string>
|
||||
<string name="solana_rent_warning">Сеть Solana взимает арендную плату в размере %1$s каждые 2 дня. Аккаунты, которые не могут позволить себе арендную плату, удаляются из сети. Пополните свой счет более чем на %2$s, чтобы не платить арендную плату.</string>
|
||||
<string name="story_awe_description">Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте.</string>
|
||||
|
|
|
|||
|
|
@ -323,11 +323,9 @@
|
|||
<string name="send_transaction_success">交易已成功簽署並發送至區塊鏈節點。錢包餘額稍後更新</string>
|
||||
<string name="send_validation_invalid_address">無效地址</string>
|
||||
<string name="shop_buy_now">現在購買</string>
|
||||
<string name="shop_free">免費</string>
|
||||
<string name="shop_i_have_a_promo_code">輸入折扣碼</string>
|
||||
<string name="shop_one_wallet">Tangem Wallet</string>
|
||||
<string name="shop_other_payment_methods">其他付款方式</string>
|
||||
<string name="shop_shipping">運送</string>
|
||||
<string name="shop_total">總計</string>
|
||||
<string name="solana_rent_warning">Solana 網絡每 2 天收取 %1$s 的費用。無法付此費用的帳戶將從網絡中清除。向您的帳戶存入超過 %2$s 即可免費使用</string>
|
||||
<string name="story_awe_description">安全地存儲您的加密貨幣,同時將私鑰保存在您的卡中</string>
|
||||
|
|
|
|||
|
|
@ -329,11 +329,10 @@
|
|||
<string name="send_transaction_success">Transaction has been successfully signed and sent to the blockchain node. Wallet balance will be updated in a while</string>
|
||||
<string name="send_validation_invalid_address">Invalid address</string>
|
||||
<string name="shop_buy_now">Buy now</string>
|
||||
<string name="shop_free">Free</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code…</string>
|
||||
<string name="shop_one_wallet">Tangem Wallet</string>
|
||||
<string name="shop_other_payment_methods">Other payment methods</string>
|
||||
<string name="shop_shipping">Shipping</string>
|
||||
<string name="shop_sold_out_description_prefix">Due to the high volume of orders we are receiving, Shipping and Local Delivery orders may be delayed.</string>
|
||||
<string name="shop_total">Total</string>
|
||||
<string name="solana_rent_warning">Solana network charges a rent of %1$s every 2 days. Accounts that can\'t afford the rent are purged from the network. Deposit your account with more than %2$s to use it for free.</string>
|
||||
<string name="story_awe_description">Store your crypto assets secure while keeping private keys contained in your card</string>
|
||||
|
|
|
|||
|
|
@ -4,16 +4,13 @@ import com.tangem.feature.swap.domain.cache.SwapDataCache
|
|||
import com.tangem.feature.swap.domain.converters.CryptoCurrencyConverter
|
||||
import com.tangem.feature.swap.domain.models.DataError
|
||||
import com.tangem.feature.swap.domain.models.SwapAmount
|
||||
import com.tangem.feature.swap.domain.models.domain.*
|
||||
import com.tangem.feature.swap.domain.models.domain.Currency
|
||||
import com.tangem.feature.swap.domain.models.domain.PermissionOptions
|
||||
import com.tangem.feature.swap.domain.models.domain.PreparedSwapConfigState
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapApproveType
|
||||
import com.tangem.feature.swap.domain.models.toStringWithRightOffset
|
||||
import com.tangem.feature.swap.domain.models.ui.*
|
||||
import com.tangem.lib.crypto.TransactionManager
|
||||
import com.tangem.lib.crypto.UserWalletManager
|
||||
import com.tangem.lib.crypto.models.ProxyFees
|
||||
import com.tangem.lib.crypto.models.ProxyFiatCurrency
|
||||
import com.tangem.lib.crypto.models.*
|
||||
import com.tangem.lib.crypto.models.transactions.SendTxResult
|
||||
import com.tangem.utils.toFiatString
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -115,12 +112,19 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
permissionOptions.approveData.approveModel.data
|
||||
}
|
||||
val result = transactionManager.sendApproveTransaction(
|
||||
networkId = networkId,
|
||||
feeAmount = permissionOptions.txFee.feeValue,
|
||||
gasLimit = permissionOptions.txFee.gasLimit,
|
||||
destinationAddress = permissionOptions.approveData.approveModel.toAddress,
|
||||
dataToSign = dataToSign,
|
||||
txData = ApproveTxData(
|
||||
networkId = networkId,
|
||||
feeAmount = permissionOptions.txFee.feeValue,
|
||||
gasLimit = permissionOptions.txFee.gasLimit,
|
||||
destinationAddress = permissionOptions.approveData.approveModel.toAddress,
|
||||
dataToSign = dataToSign,
|
||||
),
|
||||
derivationPath = derivationPath,
|
||||
analyticsData = AnalyticsData(
|
||||
feeType = permissionOptions.txFee.feeType.getNameForAnalytics(),
|
||||
tokenSymbol = permissionOptions.fromToken.symbol,
|
||||
permissionType = permissionOptions.approveType.getNameForAnalytics(),
|
||||
),
|
||||
)
|
||||
return when (result) {
|
||||
is SendTxResult.Success -> {
|
||||
|
|
@ -188,15 +192,21 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
): TxState {
|
||||
val amount = requireNotNull(toBigDecimalOrNull(amountToSwap)) { "wrong amount format, use only digits" }
|
||||
val result = transactionManager.sendTransaction(
|
||||
networkId = networkId,
|
||||
amountToSend = amount,
|
||||
currencyToSend = cryptoCurrencyConverter.convert(currencyToSend),
|
||||
feeAmount = fee.feeValue,
|
||||
gasLimit = fee.gasLimit,
|
||||
destinationAddress = swapStateData.swapModel.transaction.toWalletAddress,
|
||||
dataToSign = swapStateData.swapModel.transaction.data,
|
||||
txData = SwapTxData(
|
||||
networkId = networkId,
|
||||
amountToSend = amount,
|
||||
currencyToSend = cryptoCurrencyConverter.convert(currencyToSend),
|
||||
feeAmount = fee.feeValue,
|
||||
gasLimit = fee.gasLimit,
|
||||
destinationAddress = swapStateData.swapModel.transaction.toWalletAddress,
|
||||
dataToSign = swapStateData.swapModel.transaction.data,
|
||||
),
|
||||
isSwap = true,
|
||||
derivationPath = derivationPath,
|
||||
analyticsData = AnalyticsData(
|
||||
feeType = fee.feeType.getNameForAnalytics(),
|
||||
tokenSymbol = currencyToSend.symbol,
|
||||
),
|
||||
)
|
||||
return when (result) {
|
||||
is SendTxResult.Success -> {
|
||||
|
|
@ -587,12 +597,14 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
gasLimit = normalFeeGas,
|
||||
feeFiatFormatted = normalFiatFee,
|
||||
feeCryptoFormatted = normalCryptoFee,
|
||||
feeType = FeeType.NORMAL,
|
||||
),
|
||||
priorityFee = TxFee(
|
||||
feeValue = priorityFeeValue,
|
||||
gasLimit = priorityFeeGas,
|
||||
feeFiatFormatted = priorityFiatFee,
|
||||
feeCryptoFormatted = priorityCryptoFee,
|
||||
feeType = FeeType.PRIORITY,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -670,8 +682,8 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
private const val DEFAULT_SLIPPAGE = 2
|
||||
private const val ZERO_BALANCE = "0"
|
||||
private const val DEFAULT_BLOCKCHAIN_INCH_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
|
||||
private const val INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT = 1.4
|
||||
private const val INCREASE_GAS_LIMIT_BY = 125 // 25%
|
||||
private const val INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT = 1.0 // if need to increase fee when check isEnough
|
||||
private const val INCREASE_GAS_LIMIT_BY = 112 // 12%
|
||||
private const val USDT_SYMBOL = "USDT"
|
||||
private const val USDC_SYMBOL = "USDC"
|
||||
private const val INFINITY_SYMBOL = "∞"
|
||||
|
|
|
|||
|
|
@ -2,4 +2,11 @@ package com.tangem.feature.swap.domain.models.domain
|
|||
|
||||
enum class SwapApproveType {
|
||||
LIMITED, UNLIMITED
|
||||
}
|
||||
|
||||
fun SwapApproveType.getNameForAnalytics(): String {
|
||||
return when (this) {
|
||||
SwapApproveType.LIMITED -> "Transaction"
|
||||
SwapApproveType.UNLIMITED -> "Unlimited"
|
||||
}
|
||||
}
|
||||
|
|
@ -77,4 +77,16 @@ data class TxFee(
|
|||
val gasLimit: Int,
|
||||
val feeFiatFormatted: String,
|
||||
val feeCryptoFormatted: String,
|
||||
)
|
||||
val feeType: FeeType,
|
||||
)
|
||||
|
||||
enum class FeeType {
|
||||
NORMAL, PRIORITY
|
||||
}
|
||||
|
||||
fun FeeType.getNameForAnalytics(): String {
|
||||
return when (this) {
|
||||
FeeType.NORMAL -> "Normal"
|
||||
FeeType.PRIORITY -> "Max"
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.getActiveIconRes
|
||||
import com.tangem.core.ui.extensions.getActiveIconResByCoinId
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.domain.models.ui.FeeType
|
||||
import com.tangem.feature.swap.domain.models.ui.TxFee
|
||||
import com.tangem.feature.swap.models.*
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
|
|
@ -394,6 +395,7 @@ val stateSelectable = SelectableItemsState<TxFee>(
|
|||
gasLimit = 0,
|
||||
feeFiatFormatted = "",
|
||||
feeCryptoFormatted = "",
|
||||
feeType = FeeType.NORMAL,
|
||||
),
|
||||
),
|
||||
items = listOf(
|
||||
|
|
@ -407,6 +409,7 @@ val stateSelectable = SelectableItemsState<TxFee>(
|
|||
gasLimit = 0,
|
||||
feeFiatFormatted = "",
|
||||
feeCryptoFormatted = "",
|
||||
feeType = FeeType.NORMAL,
|
||||
),
|
||||
),
|
||||
Item(
|
||||
|
|
@ -419,6 +422,7 @@ val stateSelectable = SelectableItemsState<TxFee>(
|
|||
gasLimit = 0,
|
||||
feeFiatFormatted = "",
|
||||
feeCryptoFormatted = "",
|
||||
feeType = FeeType.NORMAL,
|
||||
),
|
||||
),
|
||||
).toImmutableList(),
|
||||
|
|
|
|||
|
|
@ -75,9 +75,9 @@ reactiveNetwork = "3.0.8"
|
|||
# endregion Other libraries
|
||||
|
||||
# region Tangem
|
||||
tangemBlockchainSdk = "develop-237"
|
||||
tangemBlockchainSdk = "develop-240"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-249"
|
||||
tangemCardSdk = "develop-266"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds
|
||||
# endregion Tangem
|
||||
|
||||
|
|
@ -179,7 +179,7 @@ appsflyer = { module = "com.appsflyer:af-android-sdk", version.ref = "appsflyer"
|
|||
armadillo = { module = "at.favre.lib:armadillo", version.ref = "armadillo" }
|
||||
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
|
||||
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutine" }
|
||||
kotlin-coroutines-reactive = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-reactive", version.ref = "coroutine" }
|
||||
kotlin-coroutines-rx2 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-rx2", version.ref = "coroutine" }
|
||||
kotlin-immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlin-immutable-collections" }
|
||||
desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" }
|
||||
googlePlay-core = { module = "com.google.android.play:core", version.ref = "googlePlayCore" }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.lib.crypto
|
||||
|
||||
import com.tangem.lib.crypto.models.Currency
|
||||
import com.tangem.lib.crypto.models.ProxyFees
|
||||
import com.tangem.lib.crypto.models.ProxyNetworkInfo
|
||||
import com.tangem.lib.crypto.models.*
|
||||
import com.tangem.lib.crypto.models.transactions.SendTxResult
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -11,26 +9,25 @@ interface TransactionManager {
|
|||
@Suppress("LongParameterList")
|
||||
@Throws(IllegalStateException::class)
|
||||
suspend fun sendApproveTransaction(
|
||||
networkId: String,
|
||||
feeAmount: BigDecimal,
|
||||
gasLimit: Int,
|
||||
destinationAddress: String,
|
||||
dataToSign: String,
|
||||
txData: ApproveTxData,
|
||||
derivationPath: String?,
|
||||
analyticsData: AnalyticsData,
|
||||
): SendTxResult
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
/**
|
||||
* Send transaction
|
||||
*
|
||||
* @param txData data to build a tx
|
||||
* @param derivationPath for select right walletManager
|
||||
* @param analyticsData data for send analytics event
|
||||
* @return result of transaction
|
||||
*/
|
||||
@Throws(IllegalStateException::class)
|
||||
suspend fun sendTransaction(
|
||||
networkId: String,
|
||||
amountToSend: BigDecimal,
|
||||
feeAmount: BigDecimal,
|
||||
gasLimit: Int,
|
||||
destinationAddress: String,
|
||||
dataToSign: String,
|
||||
txData: SwapTxData,
|
||||
isSwap: Boolean,
|
||||
currencyToSend: Currency,
|
||||
derivationPath: String?,
|
||||
analyticsData: AnalyticsData,
|
||||
): SendTxResult
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.lib.crypto.models
|
||||
|
||||
/**
|
||||
* Analytics data for send events in analytics engine
|
||||
*
|
||||
* @property feeType type of fee (min,max,normal)
|
||||
* @property tokenSymbol symbol
|
||||
* @property permissionType optional parameter used for type tx approve
|
||||
*/
|
||||
data class AnalyticsData(
|
||||
val feeType: String,
|
||||
val tokenSymbol: String,
|
||||
val permissionType: String? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.lib.crypto.models
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Tx data for create and make approve transaction
|
||||
*
|
||||
* @property networkId id of network
|
||||
* @property feeAmount amount of fee
|
||||
* @property gasLimit gasLimit for given tx
|
||||
* @property destinationAddress address to send tx
|
||||
* @property dataToSign data to sing with signer
|
||||
*/
|
||||
data class ApproveTxData(
|
||||
val networkId: String,
|
||||
val feeAmount: BigDecimal,
|
||||
val gasLimit: Int,
|
||||
val destinationAddress: String,
|
||||
val dataToSign: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.lib.crypto.models
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Tx data for create and make transaction
|
||||
*
|
||||
* @property networkId id of network
|
||||
* @property feeAmount amount of fee
|
||||
* @property gasLimit gasLimit for given tx
|
||||
* @property destinationAddress address to send tx
|
||||
* @property dataToSign data to sing with signer
|
||||
* @property amountToSend amount of tx
|
||||
* @property currencyToSend currency for tx
|
||||
*/
|
||||
data class SwapTxData(
|
||||
val networkId: String,
|
||||
val feeAmount: BigDecimal,
|
||||
val gasLimit: Int,
|
||||
val destinationAddress: String,
|
||||
val dataToSign: String,
|
||||
val amountToSend: BigDecimal,
|
||||
val currencyToSend: Currency,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue