diff --git a/app/src/main/java/com/tangem/data/network/Server.java b/app/src/main/java/com/tangem/data/network/Server.java index c31e4cf46b..8ef83ebd52 100644 --- a/app/src/main/java/com/tangem/data/network/Server.java +++ b/app/src/main/java/com/tangem/data/network/Server.java @@ -40,6 +40,14 @@ public class Server { } } + public static class ApiInfuraRopsten { + public static final String URL_INFURA_ROPSTEN = ServerURL.API_INFURA_ROPSTEN; + + public static class Method { + public static final String MAIN = "v3/613a0b14833145968b1f656240c7d245"; + } + } + public static class ApiSoChain { public static final String URL = ServerURL.API_SOCHAIN_V2; diff --git a/app/src/main/java/com/tangem/data/network/ServerApiInfura.java b/app/src/main/java/com/tangem/data/network/ServerApiInfura.java index e168293fa4..29ded17af1 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiInfura.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiInfura.java @@ -42,6 +42,8 @@ public class ServerApiInfura { public ServerApiInfura(Blockchain blockchain) { if (blockchain == Blockchain.EthereumTestNet) { infuraApi = App.Companion.getNetworkComponent().getRetrofitInfuraTestnet().create(InfuraApi.class); + } else if (blockchain == Blockchain.TokenEmv) { + infuraApi = App.Companion.getNetworkComponent().getRetrofitInfuraRopsten().create(InfuraApi.class); } } diff --git a/app/src/main/java/com/tangem/data/network/ServerURL.java b/app/src/main/java/com/tangem/data/network/ServerURL.java index b30fc802ce..dedda6858d 100644 --- a/app/src/main/java/com/tangem/data/network/ServerURL.java +++ b/app/src/main/java/com/tangem/data/network/ServerURL.java @@ -5,6 +5,7 @@ class ServerURL { static final String API_COINMARKETCAP = "https://pro-api.coinmarketcap.com/"; static final String API_INFURA = "https://mainnet.infura.io/"; static final String API_INFURA_TESTNET = "https://rinkeby.infura.io/"; + static final String API_INFURA_ROPSTEN = "https://ropsten.infura.io/"; static final String API_SOCHAIN_V2 = "https://chain.so/"; static final String API_ESTIMATEFEE = "https://estimatefee.com/"; static final String API_UPDATE_VERSION = "https://raw.githubusercontent.com/"; diff --git a/app/src/main/java/com/tangem/di/NetworkComponent.kt b/app/src/main/java/com/tangem/di/NetworkComponent.kt index b09c235aba..3d100a99e6 100644 --- a/app/src/main/java/com/tangem/di/NetworkComponent.kt +++ b/app/src/main/java/com/tangem/di/NetworkComponent.kt @@ -17,6 +17,9 @@ interface NetworkComponent { @get:Named(Server.ApiInfuraTestnet.URL_INFURA_TESTNET) val retrofitInfuraTestnet: Retrofit + @get:Named(Server.ApiInfuraRopsten.URL_INFURA_ROPSTEN) + val retrofitInfuraRopsten: Retrofit + @get:Named(Server.ApiMaticTesnet.URL_MATIC_TESTNET) val retrofitMaticTesnet: Retrofit diff --git a/app/src/main/java/com/tangem/di/NetworkModule.kt b/app/src/main/java/com/tangem/di/NetworkModule.kt index 0358c3bd9b..339a0ca5d8 100644 --- a/app/src/main/java/com/tangem/di/NetworkModule.kt +++ b/app/src/main/java/com/tangem/di/NetworkModule.kt @@ -44,6 +44,18 @@ internal class NetworkModule { return builder.build() } + @Singleton + @Provides + @Named(Server.ApiInfuraRopsten.URL_INFURA_ROPSTEN) + fun provideRetrofitInfuraRopsten(): Retrofit { + val builder = Retrofit.Builder() + .baseUrl(Server.ApiInfuraRopsten.URL_INFURA_ROPSTEN) + .addConverterFactory(GsonConverterFactory.create()) + if (BuildConfig.DEBUG) + builder.client(createOkHttpClient()) + return builder.build() + } + @Singleton @Provides @Named(Server.ApiRootstock.URL_ROOTSTOCK) diff --git a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt index 9813ee6d80..225094e7a8 100644 --- a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt +++ b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt @@ -4,7 +4,9 @@ import android.text.InputFilter import android.util.Log import com.google.gson.Gson import com.tangem.data.Blockchain +import com.tangem.data.network.ServerApiInfura import com.tangem.data.network.ServerApiTokenEmv +import com.tangem.data.network.model.InfuraResponse import com.tangem.data.network.model.TokenEmvTransferBody import com.tangem.tangem_card.data.TangemCard import com.tangem.tangem_card.reader.CardProtocol.TangemException @@ -20,6 +22,7 @@ import org.apache.commons.lang3.SerializationUtils import org.bitcoinj.core.ECKey import org.kethereum.extensions.toBytesPadded import org.kethereum.extensions.toFixedLengthByteArray +import java.math.BigDecimal import java.math.BigInteger import java.util.* @@ -30,13 +33,17 @@ class TokenEmvEngine : TokenEngine { private val TAG = TokenEmvEngine::class.java.simpleName private fun hasLinkedContract(): Boolean { - return ctx.card.issuerData != null && ctx.card.issuerData.size == 20 + return ctx.card.issuerData != null && ctx.card.issuerData.size == 44 } override fun getBlockchain(): Blockchain { return Blockchain.TokenEmv } + override fun getChainIdNum(): Int { + return EthTransaction.ChainEnum.Mainnet.value + } + override fun getBalance(): Amount? { return if (!hasBalanceInfo()) { null @@ -89,7 +96,8 @@ class TokenEmvEngine : TokenEngine { override fun defineWallet() { try { if (hasLinkedContract()) { - ctx.coinData.wallet = String.format("0x%s", BTCUtils.toHex(ctx.card.issuerData)) + val issuerData = ctx.card.issuerData + ctx.coinData.wallet = String(issuerData.copyOfRange(2, issuerData.size)) } else { ctx.coinData.wallet = calculateAddress(ctx.card.walletPublicKey) } @@ -212,6 +220,79 @@ class TokenEmvEngine : TokenEngine { } } + override fun requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks: BlockchainRequestsCallbacks) { + val serverApiInfura = ServerApiInfura(ctx.blockchain) + + val responseListener: ServerApiInfura.ResponseListener = object : ServerApiInfura.ResponseListener { + override fun onSuccess(method: String, infuraResponse: InfuraResponse) { + when (method) { + ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT -> { + var nonce = infuraResponse.result + nonce = nonce!!.substring(2) + val count = BigInteger(nonce, 16) + coinData.confirmedTXCount = count + if (serverApiInfura.isRequestsSequenceCompleted) { //getting balances after checking for pending to avoid showing old balance as verified + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.wallet, getContractAddress(ctx.card), "") + } + } + ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT -> { + var pending = infuraResponse.result + pending = pending!!.substring(2) + val count = BigInteger(pending, 16) + coinData.unconfirmedTXCount = count + if (serverApiInfura.isRequestsSequenceCompleted) { //getting balances after checking for pending to avoid showing old balance as verified + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.wallet, getContractAddress(ctx.card), "") + } + } + ServerApiInfura.INFURA_ETH_CALL -> { + try { + var balanceCap = infuraResponse.result + balanceCap = balanceCap!!.substring(2) + val l = BigInteger(balanceCap, 16) + coinData.balanceInInternalUnits = InternalAmount(l, ctx.card.tokenSymbol) + coinData.isBalanceReceived = true + // Log.i("$TAG eth_call", balanceCap) + } catch (e: java.lang.Exception) { + e.printStackTrace() + } + } + } + if (serverApiInfura.isRequestsSequenceCompleted) { + blockchainRequestsCallbacks.onComplete(!ctx.hasError()) + } else { + blockchainRequestsCallbacks.onProgress() + } + } + + override fun onFail(method: String, message: String) { + Log.e(TAG, "onFail: $method $message") + ctx.error = message + if (serverApiInfura.isRequestsSequenceCompleted) { + blockchainRequestsCallbacks.onComplete(false) + } else { + blockchainRequestsCallbacks.onProgress() + } + } + } + + serverApiInfura.setResponseListener(responseListener) + + if (validateAddress(getContractAddress(ctx.card))) { + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.wallet, "", "") + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.wallet, "", "") + } else { + ctx.error = "Smart contract address not defined" + blockchainRequestsCallbacks.onComplete(false) + } + } + + override fun requestFee(blockchainRequestsCallbacks: BlockchainRequestsCallbacks, targetAddress: String?, amount: Amount) { + val fee = Amount(BigDecimal.ONE, balanceCurrency) + coinData.minFee = fee + coinData.normalFee = fee + coinData.maxFee = fee + } + override fun requestSendTransaction(blockchainRequestsCallbacks: BlockchainRequestsCallbacks, txForSend: ByteArray?) { val jsonBody = SerializationUtils.deserialize(txForSend) val tokenEmvTransferBody = Gson().fromJson(jsonBody, TokenEmvTransferBody::class.java) @@ -229,6 +310,10 @@ class TokenEmvEngine : TokenEngine { ServerApiTokenEmv().transfer(tokenEmvTransferBody, transferObserver) } + override fun allowSelectFeeLevel(): Boolean { + return false + } + // TODO: move all below to the server fun constructTransfer(tokenEmvTransferBody: TokenEmvTransferBody, feeValue: Amount?): SignTask.TransactionToSign { val contractAddress = tokenEmvTransferBody.contract