Updated on 2026-08-14

This commit is contained in:
Tangem 2018-12-14 11:50:13 +03:00
parent dae8c94c48
commit e7c57f1e11
10 changed files with 530 additions and 337 deletions

View file

@ -42,15 +42,15 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
private var nfcManager: NfcManager? = null
private var serverApiCommon: ServerApiCommon = ServerApiCommon()
private var serverApiInfura: ServerApiInfura = ServerApiInfura()
private var serverApiElectrum: ServerApiElectrum = ServerApiElectrum()
// private var serverApiCommon: ServerApiCommon = ServerApiCommon()
// private var serverApiInfura: ServerApiInfura = ServerApiInfura()
// private var serverApiElectrum: ServerApiElectrum = ServerApiElectrum()
private lateinit var ctx: TangemContext
private lateinit var amount: CoinEngine.Amount
private var feeRequestSuccess = false
// private var balanceRequestSuccess = false
// private var balanceRequestSuccess = false
private var minFee: CoinEngine.Amount? = null
private var maxFee: CoinEngine.Amount? = null
private var normalFee: CoinEngine.Amount? = null
@ -58,7 +58,7 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
private var requestPIN2Count = 0
private var nodeCheck = true
private var dtVerified: Date? = null
private var calcSize: Int = 0
// private var calcSize: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -84,7 +84,7 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
amount = CoinEngine.Amount(intent.getStringExtra(SignPaymentActivity.EXTRA_AMOUNT), intent.getStringExtra(SignPaymentActivity.EXTRA_AMOUNT_CURRENCY))
if (ctx.blockchain == Blockchain.Token && amount.currency!="ETH")
if (ctx.blockchain == Blockchain.Token && amount.currency != "ETH")
tvIncFee.visibility = View.INVISIBLE
else
tvIncFee.visibility = View.VISIBLE
@ -103,7 +103,7 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
if (ctx.blockchain == Blockchain.Ethereum || ctx.blockchain == Blockchain.EthereumTestNet || ctx.blockchain == Blockchain.Token) {
rgFee.isEnabled = false
requestInfura(ServerApiInfura.INFURA_ETH_GAS_PRICE)
// requestInfura(ServerApiInfura.INFURA_ETH_GAS_PRICE)
} else {
rgFee.isEnabled = true
@ -114,7 +114,7 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
progressBar.visibility = View.VISIBLE
requestEstimateFee()
// requestEstimateFee()
}
// set listeners
@ -192,6 +192,33 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2)
}
val coinEngine = CoinEngineFactory.create(ctx)
coinEngine!!.requestFee(
object : CoinEngine.FeeRequestsNotifications {
override fun onComplete(success: Boolean, minFee: CoinEngine.Amount?, normalFee: CoinEngine.Amount?, maxFee: CoinEngine.Amount?) {
if (success) {
this@ConfirmPaymentActivity.minFee = minFee
this@ConfirmPaymentActivity.normalFee = normalFee
this@ConfirmPaymentActivity.maxFee = maxFee
doSetFee(rgFee.checkedRadioButtonId)
etFee.error = null
btnSend.visibility = View.VISIBLE
feeRequestSuccess = true
// balanceRequestSuccess = true
dtVerified = Date()
} else {
finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain))
}
}
override fun needTerminate(): Boolean {
return !UtilHelper.isOnline(this@ConfirmPaymentActivity)
}
},
amount
)
// request electrum listener
// val electrumBodyListener: ServerApiHelperElectrum.ElectrumRequestDataListener = object : ServerApiHelperElectrum.ElectrumRequestDataListener {
// override fun onSuccess(electrumRequest: ElectrumRequest?) {
@ -227,97 +254,97 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
// serverApiHelperElectrum.setElectrumRequestData(electrumBodyListener)
// request infura eth gasPrice listener
val infuraBodyListener: ServerApiInfura.InfuraBodyListener = object : ServerApiInfura.InfuraBodyListener {
override fun onSuccess(method: String, infuraResponse: InfuraResponse) {
when (method) {
ServerApiInfura.INFURA_ETH_GAS_PRICE -> {
var gasPrice = infuraResponse.result
gasPrice = gasPrice.substring(2)
//TODO - remove Gwei
// rounding gas price to integer gwei
val l = BigInteger(gasPrice, 16).divide(BigInteger.valueOf(1000000000L)).multiply(BigInteger.valueOf(1000000000L))
// val infuraBodyListener: ServerApiInfura.InfuraBodyListener = object : ServerApiInfura.InfuraBodyListener {
// override fun onSuccess(method: String, infuraResponse: InfuraResponse) {
// when (method) {
// ServerApiInfura.INFURA_ETH_GAS_PRICE -> {
// var gasPrice = infuraResponse.result
// gasPrice = gasPrice.substring(2)
// //TODO - remove Gwei
// // rounding gas price to integer gwei
// val l = BigInteger(gasPrice, 16).divide(BigInteger.valueOf(1000000000L)).multiply(BigInteger.valueOf(1000000000L))
//
// //val m = if (ctx.blockchain==Blockchain.Token) BigInteger.valueOf(60000) else BigInteger.valueOf(21000)
// val m = if (amount.currency != "ETH") BigInteger.valueOf(60000) else BigInteger.valueOf(21000)
// val weiMinFee = CoinEngine.InternalAmount(l.multiply(m), "wei")
// val weiNormalFee = CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(12)).divide(BigDecimal.valueOf(10)), "wei")
// val weiMaxFee = CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(15)).divide(BigDecimal.valueOf(10)), "wei")
//
// minFee = engine.convertToAmount(weiMinFee)
// normalFee = engine.convertToAmount(weiNormalFee)
// maxFee = engine.convertToAmount(weiMaxFee)
// doSetFee(rgFee.checkedRadioButtonId)
// //etFee.setText(weiNormalFee.toValueString())
// etFee.error = null
// btnSend.visibility = View.VISIBLE
// feeRequestSuccess = true
//// balanceRequestSuccess = true
// dtVerified = Date()
// }
// }
// }
//
// override fun onFail(method: String, message: String) {
// when (method) {
// ServerApiInfura.INFURA_ETH_GAS_PRICE -> {
// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain))
// }
// }
// }
// }
// serverApiInfura.setInfuraResponse(infuraBodyListener)
//val m = if (ctx.blockchain==Blockchain.Token) BigInteger.valueOf(60000) else BigInteger.valueOf(21000)
val m = if (amount.currency != "ETH") BigInteger.valueOf(60000) else BigInteger.valueOf(21000)
val weiMinFee = CoinEngine.InternalAmount(l.multiply(m), "wei")
val weiNormalFee = CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(12)).divide(BigDecimal.valueOf(10)), "wei")
val weiMaxFee = CoinEngine.InternalAmount(weiMinFee.multiply(BigDecimal.valueOf(15)).divide(BigDecimal.valueOf(10)), "wei")
minFee = engine.convertToAmount(weiMinFee)
normalFee = engine.convertToAmount(weiNormalFee)
maxFee = engine.convertToAmount(weiMaxFee)
doSetFee(rgFee.checkedRadioButtonId)
//etFee.setText(weiNormalFee.toValueString())
etFee.error = null
btnSend.visibility = View.VISIBLE
feeRequestSuccess = true
// balanceRequestSuccess = true
dtVerified = Date()
}
}
}
override fun onFail(method: String, message: String) {
when (method) {
ServerApiInfura.INFURA_ETH_GAS_PRICE -> {
finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain))
}
}
}
}
serverApiInfura.setInfuraResponse(infuraBodyListener)
// request estimate fee listener
val estimateFeeListener: ServerApiCommon.EstimateFeeListener = object : ServerApiCommon.EstimateFeeListener {
override fun onSuccess(blockCount: Int, estimateFeeResponse: String?) {
var fee: BigDecimal?
fee = BigDecimal(estimateFeeResponse) // BTC per 1 kb
if (fee == BigDecimal.ZERO) {
progressBar.visibility = View.INVISIBLE
requestEstimateFee()
}
if (calcSize.toLong() != 0L) {
fee = fee.multiply(BigDecimal(calcSize.toLong())).divide(BigDecimal(1024)) // per Kb -> per byte
} else {
requestEstimateFee()
}
progressBar.visibility = View.INVISIBLE
fee = fee!!.setScale(8, RoundingMode.DOWN)
when (blockCount) {
ServerApiCommon.ESTIMATE_FEE_MINIMAL -> {
minFee = CoinEngine.Amount(fee, engine.feeCurrency)
if (rgFee.checkedRadioButtonId == R.id.rbMinimalFee) doSetFee(rgFee.checkedRadioButtonId)
}
ServerApiCommon.ESTIMATE_FEE_NORMAL -> {
normalFee = CoinEngine.Amount(fee, engine.feeCurrency)
if (rgFee.checkedRadioButtonId == R.id.rbNormalFee) doSetFee(rgFee.checkedRadioButtonId)
}
ServerApiCommon.ESTIMATE_FEE_PRIORITY -> {
maxFee = CoinEngine.Amount(fee, engine.feeCurrency)
if (rgFee.checkedRadioButtonId == R.id.rbMaximumFee) doSetFee(rgFee.checkedRadioButtonId)
}
}
etFee.error = null
feeRequestSuccess = true
if (feeRequestSuccess)
// if (feeRequestSuccess && balanceRequestSuccess)
btnSend.visibility = View.VISIBLE
dtVerified = Date()
}
override fun onFail(message: String?) {
finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_calculate_fee_wrong_data_received_from_node))
}
}
serverApiCommon.setEstimateFee(estimateFeeListener)
// // request estimate fee listener
// val estimateFeeListener: ServerApiCommon.EstimateFeeListener = object : ServerApiCommon.EstimateFeeListener {
// override fun onSuccess(blockCount: Int, estimateFeeResponse: String?) {
// var fee: BigDecimal?
// fee = BigDecimal(estimateFeeResponse) // BTC per 1 kb
//
// if (fee == BigDecimal.ZERO) {
// progressBar.visibility = View.INVISIBLE
// requestEstimateFee()
// }
//
// if (calcSize.toLong() != 0L) {
// fee = fee.multiply(BigDecimal(calcSize.toLong())).divide(BigDecimal(1024)) // per Kb -> per byte
// } else {
// requestEstimateFee()
// }
//
// progressBar.visibility = View.INVISIBLE
//
// fee = fee!!.setScale(8, RoundingMode.DOWN)
//
// when (blockCount) {
// ServerApiCommon.ESTIMATE_FEE_MINIMAL -> {
// minFee = CoinEngine.Amount(fee, engine.feeCurrency)
// if (rgFee.checkedRadioButtonId == R.id.rbMinimalFee) doSetFee(rgFee.checkedRadioButtonId)
// }
//
// ServerApiCommon.ESTIMATE_FEE_NORMAL -> {
// normalFee = CoinEngine.Amount(fee, engine.feeCurrency)
// if (rgFee.checkedRadioButtonId == R.id.rbNormalFee) doSetFee(rgFee.checkedRadioButtonId)
// }
//
// ServerApiCommon.ESTIMATE_FEE_PRIORITY -> {
// maxFee = CoinEngine.Amount(fee, engine.feeCurrency)
// if (rgFee.checkedRadioButtonId == R.id.rbMaximumFee) doSetFee(rgFee.checkedRadioButtonId)
// }
// }
//
// etFee.error = null
// feeRequestSuccess = true
// if (feeRequestSuccess)
//// if (feeRequestSuccess && balanceRequestSuccess)
// btnSend.visibility = View.VISIBLE
// dtVerified = Date()
// }
//
// override fun onFail(message: String?) {
// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_calculate_fee_wrong_data_received_from_node))
// }
// }
// serverApiCommon.setEstimateFee(estimateFeeListener)
}
public override fun onResume() {
@ -394,98 +421,35 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
// TODO - move to BtcEngine
@Throws(Exception::class)
internal fun buildSize(outputAddress: String, outFee: String, outAmount: String): Int {
val myAddress = ctx.coinData!!.wallet
val pbKey = ctx.card!!.walletPublicKey
val pbComprKey = ctx.card!!.walletPublicKeyRar
// build script for our address
val rawTxList = (ctx.coinData!! as BtcData).unspentTransactions
val outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes
// private fun requestElectrum(ctx: TangemContext, electrumRequest: ElectrumRequest) {
// if (UtilHelper.isOnline(this)) {
// serverApiElectrum.electrumRequestData(ctx, electrumRequest)
// } else
// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain))
// }
// collect unspent
val unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend)
// private fun requestInfura(method: String) {
// if (UtilHelper.isOnline(this)) {
// serverApiInfura.infura(method, 67, ctx.coinData!!.wallet, "", "")
// } else
// finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain))
// }
var fullAmount: Long = 0
for (i in unspentOutputs.indices) {
fullAmount += unspentOutputs[i].value
}
// get first unspent
// val outPut = unspentOutputs[0]
// val outPutIndex = outPut.outputIndex
// get prev TX id;
// val prevTXID = rawTxList[0].txID//"f67b838d6e2c0c587f476f583843e93ff20368eaf96a798bdc25e01f53f8f5d2";
val fees = FormatUtil.ConvertStringToLong(outFee)
var amount = FormatUtil.ConvertStringToLong(outAmount)
amount -= fees
val change = fullAmount - fees - amount
if (amount + fees > fullAmount) {
throw Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount))
}
val hashesForSign = arrayOfNulls<ByteArray>(unspentOutputs.size)
for (i in unspentOutputs.indices) {
val newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, myAddress, unspentOutputs, i, amount, change)
val hashData = Util.calculateSHA256(newTX)
val doubleHashData = Util.calculateSHA256(hashData)
// Log.e("TX_BODY_1", BTCUtils.toHex(newTX))
// Log.e("TX_HASH_1", BTCUtils.toHex(hashData))
// Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData))
// unspentOutputs[i].bodyDoubleHash = doubleHashData
// unspentOutputs[i].bodyHash = hashData
hashesForSign[i] = doubleHashData
}
val signFromCard = ByteArray(64 * unspentOutputs.size)
for (i in unspentOutputs.indices) {
val r = BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64))
val s = BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64))
val encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey)
unspentOutputs[i].scriptForBuild = encodingSign
}
val realTX = BTCUtils.buildTXForSend(outputAddress, myAddress, unspentOutputs, amount, change)
return realTX.size
}
private fun requestElectrum(ctx: TangemContext, electrumRequest: ElectrumRequest) {
if (UtilHelper.isOnline(this)) {
serverApiElectrum.electrumRequestData(ctx, electrumRequest)
} else
finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain))
}
private fun requestInfura(method: String) {
if (UtilHelper.isOnline(this)) {
serverApiInfura.infura(method, 67, ctx.coinData!!.wallet, "", "")
} else
finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_obtain_data_from_blockchain))
}
private fun requestEstimateFee() {
if( calcSize==0 )
{
calcSize = 256
try {
calcSize = buildSize(etWallet!!.text.toString(), "0.00", etAmount.text.toString())
} catch (ex: Exception) {
Log.e("Build Fee error", ex.message)
}
}
serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY)
serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_NORMAL)
serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL)
}
// private fun requestEstimateFee() {
// if (calcSize == 0) {
// calcSize = 256
// try {
//
// calcSize = buildSize(etWallet!!.text.toString(), "0.00", etAmount.text.toString())
// } catch (ex: Exception) {
// Log.e("Build Fee error", ex.message)
// }
// }
// serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY)
// serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_NORMAL)
// serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL)
// }
private fun doSetFee(checkedRadioButtonId: Int) {
var txtFee = ""

View file

@ -271,7 +271,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco
} catch (e: Exception) {
e.printStackTrace()
nfcManager!!.notifyReadResult(false)
nfcManager.notifyReadResult(false)
}
}
@ -279,11 +279,11 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco
super.onResume()
animate()
ReadCardInfoTask.resetLastReadInfo()
nfcManager!!.onResume()
nfcManager.onResume()
}
public override fun onPause() {
nfcManager!!.onPause()
nfcManager.onPause()
if (readCardInfoTask != null) {
readCardInfoTask!!.cancel(true)
}
@ -292,7 +292,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtoco
public override fun onStop() {
// dismiss enable NFC dialog
nfcManager!!.onStop()
nfcManager.onStop()
if (readCardInfoTask != null) {
readCardInfoTask!!.cancel(true)
}

View file

@ -22,24 +22,17 @@ import android.widget.Toast
import com.tangem.App
import com.tangem.Constant
import com.tangem.data.network.ServerApiCommon
import com.tangem.data.network.ServerApiInfura
import com.tangem.tangemserver.android.model.CardVerifyAndGetInfo
import com.tangem.data.network.model.InfuraResponse
import com.tangem.tangemcard.tasks.VerifyCardTask
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.domain.wallet.*
import com.tangem.domain.wallet.bch.BtcCashEngine
import com.tangem.domain.wallet.eth.EthData
import com.tangem.domain.wallet.token.TokenData
import com.tangem.domain.wallet.token.TokenEngine
import com.tangem.presentation.activity.*
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.PINSwapWarningDialog
import com.tangem.presentation.dialog.ShowQRCodeDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.data.Blockchain
import com.tangem.data.network.ElectrumRequest
import com.tangem.tangemcard.android.reader.NfcReader
import com.tangem.tangemcard.data.EXTRA_TANGEM_CARD
import com.tangem.tangemcard.data.EXTRA_TANGEM_CARD_UID
@ -50,9 +43,7 @@ import com.tangem.tangemserver.android.ServerApiTangem
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fr_loaded_wallet.*
import org.json.JSONException
import java.io.InputStream
import java.math.BigInteger
import java.util.*
class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, SharedPreferences.OnSharedPreferenceChangeListener {
@ -802,23 +793,21 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific
requestVerifyAndGetInfo()
if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet || ctx.blockchain == Blockchain.BitcoinCash) {
val coinEngine = CoinEngineFactory.create(ctx)
requestCounter++
coinEngine!!.requestBalanceAndUnspentTransactions(
object : CoinEngine.BlockchainRequestsNotifications {
override fun onComplete(success: Boolean?) {
counterMinus()
updateViews()
}
override fun needTerminate(): Boolean {
return !UtilHelper.isOnline(context as Activity)
}
val coinEngine = CoinEngineFactory.create(ctx)
requestCounter++
coinEngine!!.requestBalanceAndUnspentTransactions(
object : CoinEngine.BalanceAndUnspentTransactionsNotifications {
override fun onComplete(success: Boolean?) {
counterMinus()
updateViews()
}
)
}
override fun needTerminate(): Boolean {
return !UtilHelper.isOnline(context as Activity)
}
}
)
// Bitcoin
if (ctx.blockchain == Blockchain.Bitcoin || ctx.blockchain == Blockchain.BitcoinTestNet) {
@ -865,15 +854,15 @@ class LoadedWallet : Fragment(), NfcAdapter.ReaderCallback, CardProtocol.Notific
// }
// }
private fun requestInfura(method: String, contract: String) {
if (UtilHelper.isOnline(context as Activity)) {
requestCounter++
serverApiInfura.infura(method, 67, ctx.coinData!!.wallet, contract, "")
} else {
Toast.makeText(activity, getString(R.string.no_connection), Toast.LENGTH_SHORT).show()
srl?.isRefreshing = false
}
}
// private fun requestInfura(method: String, contract: String) {
// if (UtilHelper.isOnline(context as Activity)) {
// requestCounter++
// serverApiInfura.infura(method, 67, ctx.coinData!!.wallet, contract, "")
// } else {
// Toast.makeText(activity, getString(R.string.no_connection), Toast.LENGTH_SHORT).show()
// srl?.isRefreshing = false
// }
// }
private fun requestVerifyAndGetInfo() {
if (UtilHelper.isOnline(context as Activity)) {