diff --git a/app/src/main/java/com/tangem/data/Blockchain.java b/app/src/main/java/com/tangem/data/Blockchain.java
index 11cb8eecf2..9e95661c57 100644
--- a/app/src/main/java/com/tangem/data/Blockchain.java
+++ b/app/src/main/java/com/tangem/data/Blockchain.java
@@ -12,6 +12,7 @@ public enum Blockchain {
Ethereum("ETH", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
EthereumTestNet("ETH/test", "ETH", 1.0, R.drawable.ic_logo_ethereum_testnet, "Ethereum Testnet"),
Token("Token", "ETH", 1.0, R.drawable.ic_logo_bat_token, "Ethereum"),
+ NftToken("NftToken", "", 1.0, R.drawable.ic_logo_bat_token, "Ethereum"),
BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.ic_logo_bitcoin_cash, "Bitcoin Cash"),
Litecoin("LTC", "LTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Litecoin"),
Rootstock("RSK", "RBTC", 1.0, R.drawable.ic_logo_bitcoin, "Rootstock"),
diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java
index 1f53e9b4bd..9340405a2e 100644
--- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java
+++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java
@@ -361,5 +361,9 @@ public abstract class CoinEngine {
return true;
}
+ public boolean isNftToken() {
+ return false;
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt b/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt
index adf693ade8..9d99f0121f 100644
--- a/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt
+++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt
@@ -8,6 +8,7 @@ import com.tangem.domain.wallet.token.TokenEngine
import com.tangem.domain.wallet.bch.BtcCashEngine
import com.tangem.data.Blockchain
import com.tangem.domain.wallet.ltc.LtcEngine
+import com.tangem.domain.wallet.nftToken.NftTokenEngine
import com.tangem.domain.wallet.rsk.RskEngine
import com.tangem.domain.wallet.rsk.RskTokenEngine
@@ -28,6 +29,7 @@ object CoinEngineFactory {
Blockchain.BitcoinCash -> BtcCashEngine()
Blockchain.Ethereum, Blockchain.EthereumTestNet -> EthEngine()
Blockchain.Token -> TokenEngine()
+ Blockchain.NftToken -> NftTokenEngine()
Blockchain.Litecoin -> LtcEngine()
Blockchain.Rootstock -> RskEngine()
Blockchain.RootstockToken -> RskTokenEngine()
@@ -47,6 +49,8 @@ object CoinEngineFactory {
EthEngine(context)
else if (Blockchain.Token == context.blockchain)
TokenEngine(context)
+ else if (Blockchain.NftToken == context.blockchain)
+ NftTokenEngine(context)
else if (Blockchain.Litecoin == context.blockchain)
LtcEngine(context)
else if (Blockchain.Rootstock == context.blockchain)
diff --git a/app/src/main/java/com/tangem/domain/wallet/TangemContext.java b/app/src/main/java/com/tangem/domain/wallet/TangemContext.java
index 7e642c01b3..1537c56082 100644
--- a/app/src/main/java/com/tangem/domain/wallet/TangemContext.java
+++ b/app/src/main/java/com/tangem/domain/wallet/TangemContext.java
@@ -11,7 +11,7 @@ import com.tangem.tangemcard.data.TangemCardExtensionsKt;
public class TangemContext {
-// public static final String EXTRA_BLOCKCHAIN_DATA = "BLOCKCHAIN_DATA";
+ // public static final String EXTRA_BLOCKCHAIN_DATA = "BLOCKCHAIN_DATA";
private Context context;
private TangemCard card;
private CoinData coinData;
@@ -28,13 +28,15 @@ public class TangemContext {
public Blockchain getBlockchain() {
if (card == null) return Blockchain.Unknown;
- Blockchain blockchain=Blockchain.fromId(card.getBlockchainID());
- if( (blockchain==Blockchain.Ethereum || blockchain==Blockchain.EthereumTestNet)&& card.isToken() )
- {
- return Blockchain.Token;
+ Blockchain blockchain = Blockchain.fromId(card.getBlockchainID());
+ if ((blockchain == Blockchain.Ethereum || blockchain == Blockchain.EthereumTestNet) && card.isToken()) {
+ if (card.getTokenSymbol().startsWith("NFT:")) {
+ return Blockchain.NftToken;
+ } else {
+ return Blockchain.Token;
+ }
}
- if( (blockchain==Blockchain.Rootstock)&& card.isToken() )
- {
+ if ((blockchain == Blockchain.Rootstock) && card.isToken()) {
return Blockchain.RootstockToken;
}
return blockchain;
@@ -48,13 +50,15 @@ public class TangemContext {
// private String blockchainName = "";
public String getBlockchainName() {
- Blockchain blockchain=getBlockchain();
- if( blockchain==Blockchain.Token || blockchain==Blockchain.RootstockToken ) {
+ Blockchain blockchain = getBlockchain();
+ if (blockchain == Blockchain.Token || blockchain == Blockchain.RootstockToken) {
String token = card.getTokenSymbol();
return token + "
" + getBlockchain().getOfficialName() + " smart contract token";
- }else {
- return blockchain.getOfficialName();
}
+ if (blockchain == Blockchain.NftToken) {
+ return card.getTokenSymbol().substring(4) + "
" + getBlockchain().getOfficialName() + " smart contract token";
+ }
+ return blockchain.getOfficialName();
}
public Context getContext() {
@@ -84,6 +88,7 @@ public class TangemContext {
public void setError(String error) {
this.error = error;
}
+
public void setError(int valueId) {
this.error = getString(valueId);
}
@@ -93,7 +98,7 @@ public class TangemContext {
}
public boolean hasError() {
- return error!=null && !error.isEmpty();
+ return error != null && !error.isEmpty();
}
public void setMessage(String value) {
@@ -154,13 +159,13 @@ public class TangemContext {
public void setDenomination(byte[] denomination) {
try {
- CoinEngine engine= CoinEngineFactory.INSTANCE.create(getBlockchain());
- CoinEngine.InternalAmount internalAmount=engine.convertToInternalAmount(denomination);
- CoinEngine.Amount amount=engine.convertToAmount(internalAmount);
- card.setDenomination(denomination,amount.toString());
+ CoinEngine engine = CoinEngineFactory.INSTANCE.create(getBlockchain());
+ CoinEngine.InternalAmount internalAmount = engine.convertToInternalAmount(denomination);
+ CoinEngine.Amount amount = engine.convertToAmount(internalAmount);
+ card.setDenomination(denomination, amount.toString());
} catch (Exception e) {
e.printStackTrace();
- card.setDenomination(denomination,"N/A");
+ card.setDenomination(denomination, "N/A");
}
}
diff --git a/app/src/main/java/com/tangem/domain/wallet/nftToken/NftTokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/nftToken/NftTokenEngine.java
new file mode 100644
index 0000000000..2e9e73e016
--- /dev/null
+++ b/app/src/main/java/com/tangem/domain/wallet/nftToken/NftTokenEngine.java
@@ -0,0 +1,359 @@
+package com.tangem.domain.wallet.nftToken;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.InputFilter;
+import android.util.Log;
+
+import com.tangem.data.Blockchain;
+import com.tangem.data.network.ServerApiInfura;
+import com.tangem.data.network.model.InfuraResponse;
+import com.tangem.domain.wallet.BTCUtils;
+import com.tangem.domain.wallet.BalanceValidator;
+import com.tangem.domain.wallet.CoinData;
+import com.tangem.domain.wallet.CoinEngine;
+import com.tangem.domain.wallet.Keccak256;
+import com.tangem.domain.wallet.TangemContext;
+import com.tangem.domain.wallet.eth.EthData;
+import com.tangem.domain.wallet.token.TokenData;
+import com.tangem.tangemcard.data.TangemCard;
+import com.tangem.tangemcard.tasks.SignTask;
+import com.tangem.wallet.R;
+
+import java.math.BigInteger;
+
+
+public class NftTokenEngine extends CoinEngine {
+
+ private static final String TAG = NftTokenEngine.class.getSimpleName();
+ public TokenData coinData = null;
+
+ public NftTokenEngine(TangemContext ctx) throws Exception {
+ super(ctx);
+ if (ctx.getCoinData() == null) {
+ coinData = new TokenData();
+ ctx.setCoinData(coinData);
+ } else if (ctx.getCoinData() instanceof TokenData) {
+ coinData = (TokenData) ctx.getCoinData();
+ } else if (ctx.getCoinData() instanceof EthData) {
+ // special case with receive card data substitution from server at the moment
+ Bundle B = new Bundle();
+ ctx.getCoinData().saveToBundle(B);
+ coinData = new TokenData();
+ coinData.loadFromBundle(B);
+ ctx.setCoinData(coinData);
+ } else {
+ throw new Exception("Invalid type of Blockchain data for " + this.getClass().getSimpleName());
+ }
+ }
+
+ public NftTokenEngine() {
+ super();
+ }
+
+ public Blockchain getBlockchain() {
+ return Blockchain.NftToken;
+ }
+
+ @Override
+ public boolean awaitingConfirmation() {
+ return false;
+ }
+
+
+ @Override
+ public String getBalanceHTML() {
+ if (hasBalanceInfo()) {
+ if (isBalanceNotZero()) {
+ return "AUTHENTIC";// + getBalanceCurrency();
+ } else {
+ return "NOT FOUND";
+ }
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public String getBalanceCurrency() {
+ return ctx.getCard().getTokenSymbol().substring(4);
+ }
+
+ @Override
+ public InputFilter[] getAmountInputFilters() {
+ return new InputFilter[0];
+ }
+
+ @Override
+ public String getOfflineBalanceHTML() {
+ return ctx.getString(R.string.not_implemented);
+ }
+
+ protected String getContractAddress(TangemCard card) {
+ return card.getContractAddress();
+ }
+
+ public boolean isNeedCheckNode() {
+ return false;
+ }
+
+ @Override
+ public String getBalanceEquivalent() {
+ return null;
+ }
+
+ @Override
+ public boolean validateAddress(String address) {
+ if (address == null || address.isEmpty()) {
+ return false;
+ }
+
+ if (!address.startsWith("0x") && !address.startsWith("0X")) {
+ return false;
+ }
+
+ if (address.length() != 42) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean isBalanceNotZero() {
+ if (coinData == null) return false;
+ if (coinData.getBalanceInInternalUnits() == null) return false;
+ return coinData.getBalanceInInternalUnits().notZero();
+ }
+
+ @Override
+ public String calculateAddress(byte[] pkUncompressed) {
+ Keccak256 kec = new Keccak256();
+ int lenPk = pkUncompressed.length;
+ if (lenPk < 2) {
+ throw new IllegalArgumentException("Uncompress public key length is invalid");
+ }
+ byte[] cleanKey = new byte[lenPk - 1];
+ for (int i = 0; i < cleanKey.length; ++i) {
+ cleanKey[i] = pkUncompressed[i + 1];
+ }
+ byte[] r = kec.digest(cleanKey);
+
+ byte[] address = new byte[20];
+ for (int i = 0; i < 20; ++i) {
+ address[i] = r[i + 12];
+ }
+
+ return String.format("0x%s", BTCUtils.toHex(address));
+ }
+
+ @Override
+ public Amount convertToAmount(InternalAmount internalAmount) throws Exception {
+ throw new Exception("Not implemented");
+ }
+
+ @Override
+ public Amount convertToAmount(String strAmount, String currency) {
+ return null;
+ }
+
+ @Override
+ public InternalAmount convertToInternalAmount(Amount amount) throws Exception {
+ throw new Exception("Not implemented");
+ }
+
+ @Override
+ public InternalAmount convertToInternalAmount(byte[] bytes) throws Exception {
+ throw new Exception("Not implemented");
+ }
+
+ @Override
+ public byte[] convertToByteArray(InternalAmount amount) throws Exception {
+ throw new Exception("Not implemented");
+ }
+
+ @Override
+ public CoinData createCoinData() {
+ return new TokenData();
+ }
+
+ @Override
+ public String getUnspentInputsDescription() {
+ return "";
+ }
+
+ @Override
+ public boolean hasBalanceInfo() {
+ return coinData != null && coinData.getBalanceInInternalUnits() != null;
+ }
+
+
+ @Override
+ public Uri getWalletExplorerUri() {
+ return Uri.parse("https://etherscan.io/token/" + getContractAddress(ctx.getCard()) + "?a=" + ctx.getCoinData().getWallet());
+ }
+
+ @Override
+ public Uri getShareWalletUri() {
+ return Uri.parse("ethereum:" + ctx.getCoinData().getWallet());
+ }
+
+ @Override
+ public boolean isExtractPossible() {
+ return false;
+ }
+
+ @Override
+ public boolean checkNewTransactionAmount(Amount amount) {
+ return false;
+ }
+
+ @Override
+ public boolean checkNewTransactionAmountAndFee(Amount amount, Amount fee, Boolean isFeeIncluded) {
+ return false;
+ }
+
+ @Override
+ public boolean validateBalance(BalanceValidator balanceValidator) {
+ if (coinData.getBalanceInInternalUnits() == null) {
+ balanceValidator.setScore(0);
+ balanceValidator.setFirstLine("No connection");
+ balanceValidator.setSecondLine("Authenticity cannot be verified. Swipe down to refresh.");
+ return false;
+ }
+
+ if (coinData.isBalanceReceived()) {
+ if (isBalanceNotZero()) {
+ balanceValidator.setScore(100);
+ balanceValidator.setFirstLine("Verified in blockchain");
+ balanceValidator.setSecondLine("");
+ } else {
+ balanceValidator.setScore(0);
+ balanceValidator.setFirstLine("Authenticity was not verified");
+ balanceValidator.setSecondLine("");
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public Amount getBalance() {
+ return null;
+ }
+
+
+ @Override
+ public String evaluateFeeEquivalent(String fee) {
+ return null;
+ }
+
+ @Override
+ public String getFeeCurrency() {
+ return null;
+ }
+
+ @Override
+ public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception {
+ throw new Exception("Not implemented");
+ }
+
+ @Override
+ public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) {
+ final ServerApiInfura serverApiInfura = new ServerApiInfura();
+ // request requestData listener
+ ServerApiInfura.ResponseListener responseListener = new ServerApiInfura.ResponseListener() {
+ @Override
+ public void onSuccess(String method, InfuraResponse infuraResponse) {
+ try {
+ if (validateAddress(getContractAddress(ctx.getCard()))) {
+ String balanceCap = infuraResponse.getResult();
+ balanceCap = balanceCap.substring(2);
+ BigInteger l = new BigInteger(balanceCap, 16);
+ coinData.setBalanceInInternalUnits(new InternalAmount(l, ctx.getCard().tokenSymbol));
+ coinData.setBalanceReceived(true);
+// Log.i("$TAG eth_call", balanceCap)
+ } else {
+ ctx.setError("Smart contract address not defined");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());
+ }
+
+ @Override
+ public void onFail(String method, String message) {
+ if (!serverApiInfura.isRequestsSequenceCompleted()) {
+ ctx.setError(message);
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+ }
+ };
+ serverApiInfura.setResponseListener(responseListener);
+
+ serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
+ }
+
+ @Override
+ public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) {
+ ServerApiInfura serverApiInfura = new ServerApiInfura();
+ // request requestData eth gasPrice listener
+ ServerApiInfura.ResponseListener responseListener = new ServerApiInfura.ResponseListener() {
+ @Override
+ public void onSuccess(String method, InfuraResponse infuraResponse) {
+ String gasPrice = infuraResponse.getResult();
+ gasPrice = gasPrice.substring(2);
+
+ // rounding gas price to integer gwei
+ BigInteger l = new BigInteger(gasPrice, 16);
+
+ Log.i(TAG, "Infura gas price: " + gasPrice + " (" + l.toString() + ")");
+ BigInteger m;
+ if (!amount.getCurrency().equals(Blockchain.Ethereum.getCurrency()))
+ m = BigInteger.valueOf(60000);
+ else m = BigInteger.valueOf(21000);
+
+ Log.i(TAG, "fee multiplier: " + m.toString());
+
+ InternalAmount weiMinFee = new InternalAmount(l.multiply(m), "wei");
+ InternalAmount weiNormalFee = new InternalAmount(l.multiply(BigInteger.valueOf(12)).divide(BigInteger.valueOf(10)).multiply(m), "wei");
+ InternalAmount weiMaxFee = new InternalAmount(l.multiply(BigInteger.valueOf(15)).divide(BigInteger.valueOf(10)).multiply(m), "wei");
+ Log.i(TAG, "min fee : " + weiMinFee.toValueString() + " wei");
+ Log.i(TAG, "normal fee: " + weiNormalFee.toValueString() + " wei");
+ Log.i(TAG, "max fee : " + weiMaxFee.toValueString() + " wei");
+
+ try {
+ coinData.minFee = convertToAmount(weiMinFee);
+ coinData.normalFee = convertToAmount(weiNormalFee);
+ coinData.maxFee = convertToAmount(weiMaxFee);
+ Log.i(TAG, "min fee : " + coinData.minFee.toString());
+ Log.i(TAG, "normal fee: " + coinData.normalFee.toString());
+ Log.i(TAG, "max fee : " + coinData.maxFee.toString());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ blockchainRequestsCallbacks.onComplete(true);
+ }
+
+ @Override
+ public void onFail(String method, String message) {
+ ctx.setError(message);
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+ };
+ serverApiInfura.setResponseListener(responseListener);
+
+ serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GAS_PRICE, 67, coinData.getWallet(), "", "");
+ }
+
+ @Override
+ public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) {
+ }
+
+ public boolean isNftToken() {
+ return true;
+ }
+
+
+}
diff --git a/app/src/main/java/com/tangem/domain/wallet/rsk/RskTokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/rsk/RskTokenEngine.java
index af34eef559..c33b7902ac 100644
--- a/app/src/main/java/com/tangem/domain/wallet/rsk/RskTokenEngine.java
+++ b/app/src/main/java/com/tangem/domain/wallet/rsk/RskTokenEngine.java
@@ -39,7 +39,7 @@ public class RskTokenEngine extends TokenEngine {
@Override
public Uri getWalletExplorerUri() {
- return Uri.parse("https://explorer.rsk.co/address/" + ctx.getCoinData().getWallet());
+ return Uri.parse("https://explorer.rsk.co/address/" + ctx.getCoinData().getWallet() + "?__tab=tokens");
} // Only RSK explorer for now
@Override
@@ -105,15 +105,11 @@ public class RskTokenEngine extends TokenEngine {
//
case ServerApiRootstock.ROOTSTOCK_ETH_CALL: {
try {
- if (validateAddress(getContractAddress(ctx.getCard()))) {
- String balanceCap = rootstockResponse.getResult();
- balanceCap = balanceCap.substring(2);
- BigInteger l = new BigInteger(balanceCap, 16);
- coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol));
+ String balanceCap = rootstockResponse.getResult();
+ balanceCap = balanceCap.substring(2);
+ BigInteger l = new BigInteger(balanceCap, 16);
+ coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol));
// Log.i("$TAG eth_call", balanceCap)
- } else {
- ctx.setError("Smart contract address not defined");
- }
if (blockchainRequestsCallbacks.allowAdvance()) {
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
@@ -147,7 +143,12 @@ public class RskTokenEngine extends TokenEngine {
};
serverApiRootstock.setResponseListener(responseListener);
- serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
+ if (validateAddress(getContractAddress(ctx.getCard()))) {
+ serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
+ } else {
+ ctx.setError("Smart contract address not defined");
+ blockchainRequestsCallbacks.onComplete(false);
+ }
}
@Override
diff --git a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java
index cf1fe4ba00..45fd76eba1 100644
--- a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java
+++ b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java
@@ -679,15 +679,13 @@ public class TokenEngine extends CoinEngine {
//
case ServerApiInfura.INFURA_ETH_CALL: {
try {
- if (validateAddress(getContractAddress(ctx.getCard()))) {
- String balanceCap = infuraResponse.getResult();
- balanceCap = balanceCap.substring(2);
- BigInteger l = new BigInteger(balanceCap, 16);
- coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol));
+
+ String balanceCap = infuraResponse.getResult();
+ balanceCap = balanceCap.substring(2);
+ BigInteger l = new BigInteger(balanceCap, 16);
+ coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol));
// Log.i("$TAG eth_call", balanceCap)
- } else {
- ctx.setError("Smart contract address not defined");
- }
+
if (blockchainRequestsCallbacks.allowAdvance()) {
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
@@ -721,7 +719,12 @@ public class TokenEngine extends CoinEngine {
};
serverApiInfura.setResponseListener(responseListener);
- serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
+ if (validateAddress(getContractAddress(ctx.getCard()))) {
+ serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
+ } else {
+ ctx.setError("Smart contract address not defined");
+ blockchainRequestsCallbacks.onComplete(false);
+ }
}
@Override
diff --git a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt
index c1d0a3eafc..1ce7f91a5b 100644
--- a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt
+++ b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt
@@ -620,6 +620,13 @@ class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback
btnExtract.backgroundTintList = inactiveColor
}
+ if (engine.isNftToken) {
+ btnLoad.isEnabled = false
+ btnLoad.backgroundTintList = inactiveColor
+ btnExtract.isEnabled = false
+ btnExtract.backgroundTintList = inactiveColor
+ }
+
// //TODO why ???
// ctx.error = null
// ctx.message = null
@@ -714,6 +721,7 @@ class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback
Blockchain.Ethereum -> "ethereum"
Blockchain.EthereumTestNet -> "ethereum"
Blockchain.Token -> "ethereum"
+ Blockchain.NftToken -> "ethereum"
Blockchain.BitcoinCash -> "bitcoin-cash"
Blockchain.Litecoin -> "litecoin"
Blockchain.Rootstock -> "bitcoin"
diff --git a/tangemcard-android/src/main/res/drawable/card_default_nft.png b/tangemcard-android/src/main/res/drawable/card_default_nft.png
new file mode 100644
index 0000000000..8f7598bc99
Binary files /dev/null and b/tangemcard-android/src/main/res/drawable/card_default_nft.png differ
diff --git a/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt b/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt
index c437f13f27..707e189b87 100644
--- a/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt
+++ b/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt
@@ -50,7 +50,9 @@ class LocalStorage
}
if (artworks.count() == 0) {
+// forceSave=true only on the last one
putResourceArtworkToCatalog(R.drawable.card_default, false)
+ putResourceArtworkToCatalog(R.drawable.card_default_nft, false)
putResourceArtworkToCatalog(R.drawable.card_ru006, false)
putResourceArtworkToCatalog(R.drawable.card_ru007, false)
putResourceArtworkToCatalog(R.drawable.card_ru011, false)
@@ -62,12 +64,12 @@ class LocalStorage
putResourceArtworkToCatalog(R.drawable.card_ru020, false)
putResourceArtworkToCatalog(R.drawable.card_ru021, false)
putResourceArtworkToCatalog(R.drawable.card_ru022, false)
- putResourceArtworkToCatalog(R.drawable.card_ru023, true)
- putResourceArtworkToCatalog(R.drawable.card_ru024, true)
- putResourceArtworkToCatalog(R.drawable.card_ru028, true)
- putResourceArtworkToCatalog(R.drawable.card_ru029, true)
- putResourceArtworkToCatalog(R.drawable.card_ru030, true)
- putResourceArtworkToCatalog(R.drawable.card_ru031, true)
+ putResourceArtworkToCatalog(R.drawable.card_ru023, false)
+ putResourceArtworkToCatalog(R.drawable.card_ru024, false)
+ putResourceArtworkToCatalog(R.drawable.card_ru028, false)
+ putResourceArtworkToCatalog(R.drawable.card_ru029, false)
+ putResourceArtworkToCatalog(R.drawable.card_ru030, false)
+ putResourceArtworkToCatalog(R.drawable.card_ru031, false)
putResourceArtworkToCatalog(R.drawable.card_ru032, true)
}
if (batchesFile.exists()) {
@@ -175,14 +177,25 @@ class LocalStorage
}
}
- private fun getDefaultArtworkBitmap(): Bitmap {
- val defaultArtworkId = context.resources.getResourceEntryName(R.drawable.card_default)
+ private fun getDefaultArtworkBitmap(card: TangemCard): Bitmap {
+ val defaultArtworkId: String
+ if (card.tokenSymbol.startsWith("NFT:")) {
+ defaultArtworkId = context.resources.getResourceEntryName(R.drawable.card_default_nft)
+ } else {
+ defaultArtworkId = context.resources.getResourceEntryName(R.drawable.card_default)
+ }
val info = artworks[defaultArtworkId]
var bitmap: Bitmap? = null
if (info != null && !info.isResource) {
bitmap = loadArtworkBitmapFromFile(defaultArtworkId)
}
- if (bitmap == null) return BitmapFactory.decodeResource(context.resources, R.drawable.card_default)
+ if (bitmap == null) {
+ if (card.tokenSymbol.startsWith("NFT:")) {
+ return BitmapFactory.decodeResource(context.resources, R.drawable.card_default_nft)
+ } else {
+ return BitmapFactory.decodeResource(context.resources, R.drawable.card_default)
+ }
+ }
return bitmap
}
@@ -236,11 +249,11 @@ class LocalStorage
if (artworkResourceId != null) {
val artworkId = context.resources.getResourceEntryName(artworkResourceId)
- return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap()
+ return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap(card)
}
- val batchInfo = batches[card.batch] ?: return getDefaultArtworkBitmap()
- val artworkId = batchInfo.artworkId ?: return getDefaultArtworkBitmap()
- return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap()
+ val batchInfo = batches[card.batch] ?: return getDefaultArtworkBitmap(card)
+ val artworkId = batchInfo.artworkId ?: return getDefaultArtworkBitmap(card)
+ return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap(card)
}
override fun applySubstitution(card: TangemCard) {