Updated on 2026-08-14

This commit is contained in:
Tangem 2020-08-27 12:14:38 +03:00
parent e83c9bc495
commit 2b768f1b00
784 changed files with 2066 additions and 106417 deletions

View file

@ -1,106 +0,0 @@
package com.tangem
import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.tangem.data.dp.PrefsManager
import com.tangem.data.local.PendingTransactionsStorage
import com.tangem.di.DaggerNetworkComponent
import com.tangem.di.DaggerToastHelperComponent
import com.tangem.di.NetworkComponent
import com.tangem.di.ToastHelperComponent
import com.tangem.server_android.data.LocalStorage
import com.tangem.tangem_card.data.Issuer
import com.tangem.tangem_sdk.android.data.Firmwares
import com.tangem.tangem_sdk.android.data.PINStorage
import com.tangem.util.Analytics
import com.tangem.wallet.BuildConfig
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
class App : Application() {
companion object {
@get:Synchronized
var instance: App? = null
private set
init {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
}
lateinit var networkComponent: NetworkComponent
lateinit var toastHelperComponent: ToastHelperComponent
lateinit var firmwaresStorage: Firmwares
lateinit var localStorage: LocalStorage
lateinit var pinStorage: PINStorage
lateinit var pendingTransactionsStorage: PendingTransactionsStorage
}
override fun onCreate() {
super.onCreate()
// initialize the singleton
instance = this
networkComponent = DaggerNetworkComponent.create()
toastHelperComponent = buildToastHelperComponent()
PrefsManager.getInstance().init(this)
// common init
if (PINStorage.needInit())
PINStorage.init(applicationContext)
initIssuers()
firmwaresStorage = Firmwares(applicationContext)
localStorage = LocalStorage(applicationContext)
pinStorage = PINStorage()
pendingTransactionsStorage = PendingTransactionsStorage(applicationContext)
if (BuildConfig.DEBUG) {
Runtime.getRuntime().exec("logcat -G 16M")
com.tangem.tangem_card.util.Log.setLogger(
object : com.tangem.tangem_card.util.LoggerInterface {
override fun i(logTag: String, message: String) {
android.util.Log.i(logTag, message)
}
override fun e(logTag: String, message: String) {
android.util.Log.e(logTag, message)
}
override fun v(logTag: String, message: String) {
android.util.Log.v(logTag, message)
}
}
)
}
Analytics.setFirebaseEnabled(this)
}
private fun buildToastHelperComponent(): ToastHelperComponent {
return DaggerToastHelperComponent.builder()
.build()
}
private fun initIssuers() {
try {
applicationContext.assets.open("issuers.json").use { `is` ->
InputStreamReader(`is`, StandardCharsets.UTF_8).use { reader ->
val listType = object : TypeToken<List<Issuer>>() {
}.type
Issuer.fillIssuers(Gson().fromJson(reader, listType))
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View file

@ -1,102 +0,0 @@
package com.tangem
import android.app.Activity
object Constant {
const val FLAVOR_TANGEM_ACCESS = "tangemAccess"
const val FLAVOR_TANGEM_CARDANO = "tangemCardano"
const val PREF_LAST_WALLET_ADDRESS = "last_wallet_address"
const val URL_TANGEM = "https://play.google.com/store/apps/details?id=com.tangem.wallet"
const val EXTRA_BLOCKCHAIN_DATA = "BLOCKCHAIN_DATA"
const val WALLET_ADDRESS = "Wallet address"
const val EXTRA_MESSAGE = "message"
const val EXTRA_MODIFICATION = "modification"
const val EXTRA_MODIFICATION_DELETE = "delete"
const val EXTRA_MODIFICATION_UPDATE = "update"
const val EXTRA_MODE = "mode"
const val INTENT_TYPE_TEXT_PLAIN = "text/plain"
// LoadedWalletFragment, VerifyCardFragment
const val REQUEST_CODE_SEND_TRANSACTION = "REQUEST_CODE_SEND_TRANSACTION"
const val REQUEST_CODE_PURGE = "REQUEST_CODE_PURGE"
const val REQUEST_CODE_REQUEST_PIN2_FOR_PURGE = "REQUEST_CODE_REQUEST_PIN2_FOR_PURGE"
const val REQUEST_CODE_VERIFY_CARD = "REQUEST_CODE_VERIFY_CARD"
const val REQUEST_CODE_ENTER_NEW_PIN = "REQUEST_CODE_ENTER_NEW_PIN"
const val REQUEST_CODE_ENTER_NEW_PIN2 = "REQUEST_CODE_ENTER_NEW_PIN2"
const val REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN = "REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN"
const val REQUEST_CODE_SWAP_PIN = "REQUEST_CODE_SWAP_PIN"
const val REQUEST_CODE_RECEIVE_TRANSACTION = "REQUEST_CODE_RECEIVE_TRANSACTION"
// MainFragment
const val REQUEST_CODE_SHOW_CARD_ACTIVITY = "REQUEST_CODE_SHOW_CARD_ACTIVITY"
const val REQUEST_CODE_ENTER_PIN_ACTIVITY = "REQUEST_CODE_ENTER_PIN_ACTIVITY"
const val REQUEST_CODE_SEND_EMAIL = "REQUEST_CODE_SEND_EMAIL"
const val REQUEST_CODE_REQUEST_CAMERA_PERMISSIONS = 3
const val EXTRA_LAST_DISCOVERED_TAG = "extra_last_tag"
const val EXTRA_PIN2 = "PIN2"
// LogoFragment
const val EXTRA_AUTO_HIDE = "extra_auto_hide"
const val MILLIS_AUTO_HIDE = 1000
// PinRequestFragment
const val KEY_ALIAS = "pinKey"
const val KEYSTORE = "AndroidKeyStore"
// QrScanFragment
const val EXTRA_QR_CODE = "QRCode"
// PinSwapFragment
const val EXTRA_CONFIRM_PIN = "confirmPIN"
const val EXTRA_CONFIRM_PIN_2 = "confirmPIN2"
const val EXTRA_NEW_PIN = "newPIN"
const val EXTRA_NEW_PIN_2 = "newPIN2"
// CreateNewWalletFragment
const val RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER
// EmptyWalletFragment
const val REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY = "REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY"
const val REQUEST_CODE_REQUEST_PIN2 = "REQUEST_CODE_REQUEST_PIN2"
// ConfirmTransactionFragment
const val REQUEST_CODE_SIGN_TRANSACTION = "REQUEST_CODE_SIGN_TRANSACTION"
const val REQUEST_CODE_REQUEST_PIN2_ = "REQUEST_CODE_REQUEST_PIN2_"
// SendTransactionFragment
const val EXTRA_TX: String = "TX"
// SignTransactionFragment
const val EXTRA_AMOUNT = "Amount"
const val EXTRA_AMOUNT_CURRENCY = "AmountCurrency"
const val EXTRA_FEE = "Fee"
const val EXTRA_FEE_CURRENCY = "FeeCurrency"
const val EXTRA_FEE_INCLUDED = "FeeIncluded"
const val EXTRA_TARGET_ADDRESS = "TargetAddress"
const val REQUEST_CODE_SEND_TRANSACTION_ = "REQUEST_CODE_SEND_TRANSACTION_"
const val RESULT_INVALID_PIN_ = Activity.RESULT_FIRST_USER
// PrepareTransactionFragment
const val REQUEST_CODE_SCAN_QR = "REQUEST_CODE_SCAN_QR"
const val REQUEST_CODE_SEND_TRANSACTION__ = "REQUEST_CODE_SEND_TRANSACTION__"
// PrepareCryptonitOtherApiWithdrawalFragment
const val REQUEST_CODE_SCAN_QR_KEY = "REQUEST_CODE_SCAN_QR_KEY"
const val REQUEST_CODE_SCAN_QR_SECRET = "REQUEST_CODE_SCAN_QR_SECRET"
const val REQUEST_CODE_SCAN_QR_USER_ID = "REQUEST_CODE_SCAN_QR_USER_ID"
const val TERMINAL_PRIVATE_KEY = "terminalPrivateKey"
const val TERMINAL_PUBLIC_KEY = "terminalPublicKey"
}

View file

@ -1,128 +0,0 @@
package com.tangem.data;
import com.tangem.tangem_sdk.R;
/**
* Created by dvol on 06.08.2017.
*/
public enum Blockchain {
Unknown("", "", 1.0, R.drawable.ic_logo_unknown, "Unknown"),
Bitcoin("BTC", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Bitcoin"),
BitcoinTestNet("BTC/test", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin_testnet, "Bitcoin Testnet"),
BitcoinDual("BTC/dual", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Bitcoin"),
Ethereum("ETH", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
EthereumId("ETH/ID", "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_ethereum, "Ethereum"),
NftToken("NftToken", "", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.ic_logo_bitcoin_cash, "Bitcoin Cash"),
Litecoin("LTC", "LTC", 100000000.0, R.drawable.ic_logo_litecoin, "Litecoin"),
Rootstock("RSK", "RBTC", 1.0, R.drawable.tangem2, "RSK"),
RootstockToken("RskToken", "RBTC", 1.0, R.drawable.tangem2, "RSK"),
Cardano("CARDANO", "ADA", 1000000.0, R.drawable.tangem2, "Cardano"),
Ripple("XRP", "XRP", 1000000.0, R.drawable.ic_logo_xrp, "XRP Ledger"),
Binance("BINANCE", "BNB", 100000000.0, R.drawable.ic_logo_binance, "Binance"),
BinanceTestNet("BINANCE/test", "BNB", 100000000.0, R.drawable.ic_logo_binance, "Binance Testnet"),
BinanceAsset("BinanceAsset", "BNB", 100000000.0, R.drawable.ic_logo_binance, "Binance"),
Matic("MATIC", "MTX", 1.0, R.drawable.tangem2, "Matic"),
MaticTestNet("MATIC/test", "MTX", 1.0, R.drawable.tangem2, "Matic Testnet"),
Stellar("XLM", "XLM", 10000000.0, R.drawable.ic_logo_stellar, "Stellar"),
StellarTestNet("XLM/test", "XLM", 10000000.0, R.drawable.ic_logo_stellar, "Stellar Testnet"),
StellarAsset("Asset", "XLM", 10000000.0, R.drawable.ic_logo_stellar, "Stellar"),
StellarTag("XLM-Tag", "XLM", 1000000.0, R.drawable.ic_logo_stellar, "Stellar"),
Eos("EOS", "EOS", 10000.0, R.drawable.tangem2, "EOS"),
Ducatus("DUC", "DUC", 100000000.0, R.drawable.tangem2, "Ducatus"),
Tezos("TEZOS", "XTZ", 10000000.0, R.drawable.ic_logo_tezos, "Tezos"),
FlowDemo("FLOW/demo", "", 1.0, R.drawable.tangem2, "Flow demo"),
TokenEmv("TTW", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum");
Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) {
mID = ID;
mCurrency = currency;
// mMultiplier = multiplier;
mImageResource = imageResource;
mOfficialName = officialName;
}
private String mID, mOfficialName;
//private double mMultiplier;
private String mCurrency;
private int mImageResource;
public String getID() {
return mID;
}
public String getOfficialName() {
return mOfficialName;
}
// public double getMultiplier() {
// return mMultiplier;
// }
public String getCurrency() {
return mCurrency;
}
public static Blockchain fromId(String id) {
for (Blockchain blockchain : values()) {
if (blockchain.getID().equals(id)) return blockchain;
}
return Blockchain.Unknown;
}
public static Blockchain fromCurrency(String currency) {
for (Blockchain blockchain : values()) {
if (blockchain.getCurrency() == currency) return blockchain;
}
return null;
}
public static String[] getCurrencies() {
String[] result = new String[values().length - 1];
for (int i = 0; i < result.length - 1; i++) {
result[i] = values()[i + 1].getCurrency();
}
return result;
}
private int getImageResource() {
return mImageResource;
}
public int getLogoImageResource(String symbolName) {
switch (this) {
case Token:
if (symbolName.equals("SEED"))
return R.drawable.ic_logo_seed;
break;
case RootstockToken:
if (symbolName.equals("RIF"))
return R.drawable.ic_logo_rif;
}
return getImageResource();
}
public String getUriScheme() {
String scheme = null;
switch (this) {
case Bitcoin:
case BitcoinDual:
scheme = "bitcoin";
break;
case Ethereum:
case Token:
case TokenEmv:
scheme = "ethereum";
break;
case Litecoin:
scheme = "litecoin";
break;
case Ripple:
scheme = "ripple";
}
return scheme;
}
}

View file

@ -1,103 +0,0 @@
package com.tangem.data;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import com.tangem.wallet.R;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Objects;
/**
* Created by dvol on 15.02.2018.
*/
public class LogFileProvider extends ContentProvider {
private static final String TAG = LogFileProvider.class.getSimpleName() + "-oF";
// UriMatcher used to match against incoming requests
private UriMatcher uriMatcher;
@Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Add a URI to the matcher which will match against the form
// 'content://it.my.app.LogFileProvider/*'
// and return 1 in the case that the incoming Uri matches this pattern
uriMatcher.addURI(Objects.requireNonNull(getContext()).getString(R.string.log_file_provider_authorities), "*", 1);
return true;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
Log.v(TAG, "Called with uri: '" + uri + "'." + uri.getLastPathSegment());
// check incoming Uri against the matcher
switch (uriMatcher.match(uri)) {
// If it returns 1 - then it matches the Uri defined in onCreate
case 1:
// The desired file name is specified by the last segment of the
// path
// E.g.
// 'content://it.my.app.LogFileProvider/Test1.txt'
// Take this and build the path to the file
String fileLocation = getContext().getCacheDir() + File.separator
+ uri.getLastPathSegment();
// Create & return a ParcelFileDescriptor pointing to the file
// Note: I don't care what mode they ask for - they're only getting
// read only
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
// Otherwise unrecognised Uri
default:
Log.v(TAG, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
}
}
// //////////////////////////////////////////////////////////////
// Not supported / used / required for this example
// //////////////////////////////////////////////////////////////
@Override
public int update(Uri uri, ContentValues contentvalues, String s,
String[] as) {
return 0;
}
@Override
public int delete(Uri uri, String s, String[] as) {
return 0;
}
@Override
public Uri insert(Uri uri, ContentValues contentvalues) {
return null;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Cursor query(Uri uri, String[] projection, String s, String[] as1,
String s1) {
return null;
}
}

View file

@ -1,285 +0,0 @@
package com.tangem.data;
import android.content.Context;
import android.util.Log;
import com.tangem.tangem_card.util.Util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Date;
public class Logger {
public static File collectLogs(Context context) {
File f = new File(context.getCacheDir().getAbsolutePath() + "/Wallet_" + Util.formatDateTimeToFileName(new Date()) + ".log");
try {
if (f.createNewFile()) {
f.setReadable(true);
FileWriter fileWriter = new FileWriter(f, true);
Process process = Runtime.getRuntime().exec("logcat -d -v time");
try {
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader bufferedReader = new BufferedReader(isr);
BufferedWriter buf = new BufferedWriter(fileWriter);
buf.append("Tangem Wallet logs");
buf.newLine();
int i = 0;
String line;
while ((line = bufferedReader.readLine()) != null) {
buf.append(line);
buf.newLine();
i++;
}
Log.e("Logger", String.format("%d log lines collected", i));
buf.newLine();
buf.flush();
buf.close();
} finally {
process.destroy();
}
return f;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
//public class Logger {
//
// public static File[] getLastLogFiles() {
// File path = new File(Environment.getExternalStorageDirectory(), "Tangem/logs");
// if (!path.exists()) {
// return null;
// }
// File[] files = path.listFiles();
// Arrays.sort(files, new Comparator<File>() {
// @Override
// public int compare(File o1, File o2) {
// if (o1.lastModified() < o2.lastModified()) {
// return -1;
// } else if (o1.lastModified() > o2.lastModified()) {
// return 1;
// }
// return 0;
// }
// });
// if (files.length < 5) return files;
// return Arrays.copyOfRange(files, files.length - 5, files.length);
// }
//
// private static File logFile = null;
//
// private static void initLogFile(Context context) {
// try {
// File path = new File(Environment.getExternalStorageDirectory(), "Tangem/logs");
// if (!path.exists()) {
// path.mkdirs();
// MediaScannerConnection.scanFile(context, new String[]{path.getParentFile().toString()}, null, null);
// }
// logFile = new File(path, String.format("wallet_%s.log", Util.formatDateTimeToFileName(new Date())));
// logFile.createNewFile();
// logFile.setReadable(true);
//
// // initiate media scan and put the new things into the path array to
// // make the scanner aware of the location and the files you want to see
// MediaScannerConnection.scanFile(context, new String[]{logFile.getAbsolutePath()}, null, null);
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// }
//
// public static boolean isCurrent(File f) {
// if (f == null || logFile == null) return false;
// return f.getAbsolutePath().equals(logFile.getAbsolutePath());
// }
//
//
// private static class LogCatThread extends Thread {
// private boolean Terminated;
//
// private static final Object oSync = new Object();
//
// public void Terminate() {
// Terminated = true;
// synchronized (oSync) {
// oSync.notifyAll();
// }
// try {
// join(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// interrupt();
// }
// }
//
// public void collectLogs(Writer out) {
// try {
// Process process = Runtime.getRuntime().exec("logcat -d -b main -v time");
// try {
// InputStream is = process.getInputStream();
// InputStreamReader isr = new InputStreamReader(is);
// BufferedReader bufferedReader = new BufferedReader(isr);
// try {
//
// try {
// //BufferedWriter for performance, true to set append to file flag
// BufferedWriter buf = new BufferedWriter(out);
//
// while (!Terminated && isr.ready()) {
// String line = bufferedReader.readLine();
// buf.append(line);
// buf.newLine();
// }
// //Log.i("Logger",String.format("%d lines added",i));
// buf.newLine();
// buf.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
//
//
// } catch (Exception e) {
// e.printStackTrace();
// }
//
// } finally {
// process.destroy();
// }
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// }
//
// @Override
// public void run() {
// Process process = null;
// try {
// if (logFile == null) return;
// process = Runtime.getRuntime().exec("logcat -b main -v time");
// try {
// InputStream is = process.getInputStream();
// InputStreamReader isr = new InputStreamReader(is);
// BufferedReader bufferedReader = new BufferedReader(isr);
// while (!Terminated) {
// try {
// synchronized (oSync) {
// oSync.wait(1000);
// }
// if (!logFile.exists()) {
// try {
// logFile.createNewFile();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// try {
// //BufferedWriter for performance, true to set append to file flag
// BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
//
// while (isr.ready()) {
// String line = bufferedReader.readLine();
// buf.append(line);
// buf.newLine();
// }
// //Log.i("Logger",String.format("%d lines added",i));
// buf.newLine();
// buf.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
//
//
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// } finally {
// process.destroy();
// }
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// }
//
// static LogCatThread t = new LogCatThread();
//
// public static void StartSaveToFile(Activity activity) {
// try {
// if (logFile != null) return;
//
// verifyStoragePermissions(activity);
// initLogFile(activity.getApplicationContext());
// t.init();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//
// public static void StopSaveToFile(Context context) {
// final Object oSync = new Object();
// if (t.isAlive()) {
// t.Terminate();
// }
// if (logFile != null) {
// MediaScannerConnection.scanFile(context, new String[]{logFile.getAbsolutePath().toString()}, null, new MediaScannerConnection.OnScanCompletedListener() {
// @Override
// public void onScanCompleted(String path, Uri uri) {
//// synchronized (oSync) {
//// oSync.notifyAll();
//// }
// }
// });
//// try {
//// synchronized (oSync) {
//// oSync.wait(10000);
// logFile = null;
//// }
//// } catch (InterruptedException e) {
//// e.printStackTrace();
//// }
// }
//
// }
//
// // Storage Permissions
// private static final int REQUEST_EXTERNAL_STORAGE = 1;
// private static String[] PERMISSIONS_STORAGE = {
// Manifest.permission.READ_EXTERNAL_STORAGE,
// Manifest.permission.WRITE_EXTERNAL_STORAGE
// };
//
// //Checks if the app has permission to write to device storage
// //If the app does not has permission then the user will be prompted to grant permissions
// public static void verifyStoragePermissions(Activity activity) {
// // check if we have write permission
// int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
//
// if (permission != PackageManager.PERMISSION_GRANTED) {
// // We don't have permission so prompt the user
// ActivityCompat.requestPermissions(
// activity,
// PERMISSIONS_STORAGE,
// REQUEST_EXTERNAL_STORAGE
// );
// }
// }
//
//}

View file

@ -1,33 +0,0 @@
package com.tangem.data
import java.util.*
private val payIdSupported = EnumSet.of(
Blockchain.Ripple,
Blockchain.Ethereum,
Blockchain.Bitcoin,
Blockchain.Token,
Blockchain.Litecoin,
Blockchain.Stellar,
Blockchain.StellarAsset,
Blockchain.Cardano,
Blockchain.Ducatus,
Blockchain.BitcoinCash,
Blockchain.Binance,
Blockchain.BinanceAsset,
Blockchain.Rootstock,
Blockchain.RootstockToken
)
fun Blockchain.isPayIdSupported(): Boolean {
return payIdSupported.contains(this)
}
fun Blockchain.getPayIdNetwork(): String {
return when (this) {
Blockchain.Ripple -> "XRPL"
Blockchain.Rootstock, Blockchain.RootstockToken -> "RSK"
else -> this.currency
}
}

View file

@ -1,71 +0,0 @@
package com.tangem.data.dp
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import com.orhanobut.hawk.Hawk
import com.tangem.Constant
import com.tangem.tangem_card.reader.CardCrypto
class PrefsManager {
companion object {
const val PREF_NAME = "tangem_access"
@SuppressLint("StaticFieldLeak")
private var instance: PrefsManager? = null
fun getInstance(): PrefsManager {
if (instance == null) {
instance = PrefsManager()
}
return instance as PrefsManager
}
}
private lateinit var context: Context
private lateinit var preferences: SharedPreferences
fun init(context: Context) {
this.context = context
preferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
Hawk.init(context).build()
}
val lastWalletAddress: String
get() = Hawk.get(Constant.PREF_LAST_WALLET_ADDRESS, "")
fun saveLastWalletAddress(value: String) {
Hawk.put(Constant.PREF_LAST_WALLET_ADDRESS, value)
}
fun clearLastWalletAddress() {
Hawk.delete(Constant.PREF_LAST_WALLET_ADDRESS)
}
fun getSettingsBoolean(key: Int, default: Boolean): Boolean {
return PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(key), default)
}
fun appendCid(newCid: String) {
val prevCids: String = Hawk.get("CID_Key", "")
if (!prevCids.contains(newCid)) Hawk.put("CID_Key", "$prevCids$newCid, ")
}
fun getAllCids(): String = Hawk.get("CID_Key", "")
val terminalKeys: Map<String, ByteArray>
get() {
val privateKey = Hawk.get<ByteArray>(Constant.TERMINAL_PRIVATE_KEY) ?: byteArrayOf()
val publicKey = Hawk.get<ByteArray>(Constant.TERMINAL_PUBLIC_KEY) ?: byteArrayOf()
return if (privateKey.isNotEmpty() && publicKey.isNotEmpty()) {
mapOf(Constant.TERMINAL_PRIVATE_KEY to privateKey, Constant.TERMINAL_PUBLIC_KEY to publicKey)
} else {
val keys = CardCrypto.generateTerminalKeys()
Hawk.put(Constant.TERMINAL_PRIVATE_KEY, keys[Constant.TERMINAL_PRIVATE_KEY])
Hawk.put(Constant.TERMINAL_PUBLIC_KEY, keys[Constant.TERMINAL_PUBLIC_KEY])
keys
}
}
}

View file

@ -1,61 +0,0 @@
package com.tangem.data.fingerprint;
import android.os.AsyncTask;
import android.widget.Toast;
import com.tangem.ui.fragment.pin.PinSaveFragment;
import com.tangem.wallet.R;
import java.lang.ref.WeakReference;
import javax.crypto.Cipher;
public class ConfirmWithFingerprintTask extends AsyncTask<Void, Void, Boolean> {
private WeakReference<PinSaveFragment> reference;
public ConfirmWithFingerprintTask(PinSaveFragment context) {
reference = new WeakReference<>(context);
reference.get().setFingerprintHelper(new FingerprintHelper(reference.get()));
}
@Override
protected Boolean doInBackground(Void... params) {
PinSaveFragment pinSaveFragment = reference.get();
if (!pinSaveFragment.getKeyStore())
return false;
if (!pinSaveFragment.createNewKey(false))
return false;
if (!pinSaveFragment.getCipher())
return false;
return pinSaveFragment.initCipher(Cipher.ENCRYPT_MODE) && pinSaveFragment.initCryptObject();
}
@Override
protected void onPostExecute(final Boolean success) {
PinSaveFragment pinSaveFragment = reference.get();
onCancelled();
if (!success) {
Toast.makeText(pinSaveFragment.getContext(), R.string.pin_save_notification_failed, Toast.LENGTH_LONG).show();
} else {
pinSaveFragment.getFingerprintHelper().startAuth(pinSaveFragment.getFingerprintManager(), pinSaveFragment.getCryptoObject());
}
}
@Override
protected void onCancelled() {
PinSaveFragment pinSaveFragment = reference.get();
if (pinSaveFragment.getDFingerPrintConfirmation() != null) {
pinSaveFragment.getDFingerPrintConfirmation().cancel();
}
}
}

View file

@ -1,62 +0,0 @@
package com.tangem.data.fingerprint;
import android.annotation.TargetApi;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.CancellationSignal;
/**
* Created by dtaka on 8/20/2016.
*/
@TargetApi(Build.VERSION_CODES.M)
public class FingerprintHelper extends FingerprintManager.AuthenticationCallback {
private FingerprintHelperListener listener;
public FingerprintHelper(FingerprintHelperListener listener) {
this.listener = listener;
}
private CancellationSignal cancellationSignal;
public void startAuth(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) {
cancellationSignal = new CancellationSignal();
try {
manager.authenticate(cryptoObject, cancellationSignal, 0, this, null);
} catch (SecurityException ex) {
listener.authenticationFailed("An error occurred:\n" + ex.getMessage());
} catch (Exception ex) {
listener.authenticationFailed("An error occurred\n" + ex.getMessage());
}
}
public void cancel() {
if (cancellationSignal != null)
cancellationSignal.cancel();
}
public interface FingerprintHelperListener {
public void authenticationFailed(String error);
public void authenticationSucceeded(FingerprintManager.AuthenticationResult result);
}
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
listener.authenticationFailed("Authentication error\n" + errString);
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
listener.authenticationFailed("Authentication help\n" + helpString);
}
@Override
public void onAuthenticationFailed() {
listener.authenticationFailed("Authentication failed.");
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
listener.authenticationSucceeded(result);
}
}

View file

@ -1,193 +0,0 @@
package com.tangem.data.fingerprint;
import android.annotation.TargetApi;
import android.hardware.fingerprint.FingerprintManager;
import android.os.AsyncTask;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import com.tangem.Constant;
import com.tangem.tangem_sdk.android.data.PINStorage;
import com.tangem.ui.fragment.pin.PinRequestFragment;
import com.tangem.util.LOG;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
public class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
public static final String TAG = StartFingerprintReaderTask.class.getSimpleName();
private WeakReference<PinRequestFragment> reference;
private KeyStore keyStore;
private Cipher cipher;
private FingerprintManager.CryptoObject cryptoObject;
FingerprintManager fingerprintManager;
FingerprintHelper fingerprintHelper;
PinRequestFragment fragment;
public StartFingerprintReaderTask(PinRequestFragment fragment, FingerprintManager fingerprintManager, FingerprintHelper fingerprintHelper) {
reference = new WeakReference<>(fragment);
this.fingerprintManager = fingerprintManager;
this.fingerprintHelper = fingerprintHelper;
this.fragment = fragment;
}
@Override
protected Boolean doInBackground(Void... params) {
if (!getKeyStore())
return false;
if (!createNewKey(false))
return false;
if (!getCipher())
return false;
if (!initCipher(Cipher.DECRYPT_MODE))
return false;
return initCryptObject();
}
@Override
protected void onPostExecute(final Boolean success) {
onCancelled();
if (!success) {
LOG.i(TAG, "Authentication failed!");
} else {
fingerprintHelper.startAuth(fingerprintManager, cryptoObject);
LOG.i(TAG, "Authenticate using fingerprint!");
}
}
@Override
protected void onCancelled() {
PinRequestFragment fragment = reference.get();
fragment.setStartFingerprintReaderTask(null);
}
private boolean getKeyStore() {
LOG.i(TAG, "Getting keystore...");
try {
keyStore = KeyStore.getInstance(Constant.KEYSTORE);
keyStore.load(null); // Create empty keystore
return true;
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
public boolean createNewKey(boolean forceCreate) {
LOG.i(TAG, "Creating new key...");
try {
if (forceCreate)
keyStore.deleteEntry(Constant.KEY_ALIAS);
if (!keyStore.containsAlias(Constant.KEY_ALIAS)) {
KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, Constant.KEYSTORE);
generator.init(new KeyGenParameterSpec.Builder(Constant.KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.build()
);
generator.generateKey();
LOG.i(TAG, "Key created.");
} else
LOG.i(TAG, "Key exists.");
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private boolean getCipher() {
LOG.i(TAG, "Getting cipher...");
try {
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
return true;
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
private boolean initCipher(int mode) {
PinRequestFragment fragment = reference.get();
LOG.i(TAG, "Initializing cipher...");
try {
keyStore.load(null);
SecretKey keyspec = (SecretKey) keyStore.getKey(Constant.KEY_ALIAS, null);
if (mode == Cipher.ENCRYPT_MODE) {
cipher.init(mode, keyspec);
} else {
byte[] iv = null;
if (fragment.getMode() == PinRequestFragment.Mode.RequestPIN ||
fragment.getMode() == PinRequestFragment.Mode.RequestNewPIN ||
fragment.getMode() == PinRequestFragment.Mode.ConfirmNewPIN) {
iv = PINStorage.loadEncryptedIV();
} else if (fragment.getMode() == PinRequestFragment.Mode.RequestPIN2 ||
fragment.getMode() == PinRequestFragment.Mode.RequestNewPIN2 ||
fragment.getMode() == PinRequestFragment.Mode.ConfirmNewPIN2) {
iv = PINStorage.loadEncryptedIV2();
}
IvParameterSpec ivspec = new IvParameterSpec(iv);
cipher.init(mode, keyspec, ivspec);
}
return true;
} catch (KeyPermanentlyInvalidatedException e) {
e.printStackTrace();
createNewKey(true); // Retry after clearing entry
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
private boolean initCryptObject() {
LOG.i(TAG, "Initializing crypt object...");
try {
cryptoObject = new FingerprintManager.CryptoObject(cipher);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}

View file

@ -1,106 +0,0 @@
package com.tangem.data.local
import android.content.Context
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.tangem.tangem_card.data.TangemCard
import java.io.File
import java.util.*
import kotlin.collections.HashMap
class PendingTransactionsStorage
(
val context: Context
) {
private lateinit var cardTransactions: MutableMap<String, CardTransactionsInfo>
private val transactionsFile: File = File(context.filesDir, "transactions.json")
private var cacheDir: File? = null
init {
cacheDir = File(context.filesDir, "artworks")
if (!cacheDir!!.exists())
cacheDir!!.mkdirs()
if (transactionsFile.exists()) {
try {
transactionsFile.bufferedReader().use { cardTransactions = Gson().fromJson(it, object : TypeToken<HashMap<String, CardTransactionsInfo>>() {}.type) }
} catch (e: Exception) {
e.printStackTrace()
cardTransactions = HashMap()
}
} else {
cardTransactions = HashMap()
}
}
private fun clearExpired()
{
for(cardId in cardTransactions.keys) {
cardTransactions[cardId]!!.transactions.removeAll { it.isExpired() }
}
cardTransactions.entries.removeAll { it.value.isEmpty() }
}
fun save() {
clearExpired()
val sTransactions = Gson().toJson(cardTransactions)
transactionsFile.bufferedWriter().use { it.write(sTransactions) }
}
fun putTransaction(card: TangemCard, txId: String, expireTimeoutInSeconds: Int) {
val sendDate=Date()
val calendar=Calendar.getInstance()
calendar.time=sendDate
calendar.add(Calendar.SECOND, expireTimeoutInSeconds)
val expireDate=calendar.time
putTransaction(card.cidDescription, txId, sendDate, expireDate)
}
private fun putTransaction(cardId: String, txId: String, sendDate: Date, expireDate: Date) {
val transactionInfo = TransactionInfo(
txId, sendDate, expireDate
)
if (cardTransactions[cardId] == null) {
cardTransactions[cardId] = CardTransactionsInfo(arrayListOf())
}
cardTransactions[cardId]?.transactions?.add(transactionInfo)
save()
}
fun getTransactions(card: TangemCard): CardTransactionsInfo? {
clearExpired()
return cardTransactions[card.cidDescription]
}
fun hasTransactions(card: TangemCard): Boolean {
val cardTransactionsInfo=getTransactions(card)
if( cardTransactionsInfo!=null ) return cardTransactionsInfo.transactions.count()>0
return false
}
fun removeTransaction(card: TangemCard, txId: String)
{
cardTransactions[card.cidDescription]?.transactions?.removeAll { it.tx==txId }
save()
}
data class TransactionInfo(
val tx: String,
val sendDate: Date,
val expireDate: Date
) {
fun isExpired(): Boolean {
return Date().after(expireDate)
}
}
data class CardTransactionsInfo(
var transactions: MutableList<TransactionInfo>
) {
fun isEmpty(): Boolean {
return transactions.count()==0
}
}
}

View file

@ -1,30 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.AdaliteBody;
import com.tangem.data.network.model.AdaliteResponse;
import com.tangem.data.network.model.AdaliteResponseUtxo;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface AdaliteApi {
@GET(ServerApiAdalite.ADALITE_ADDRESS)
Call<AdaliteResponse> adaliteAddress(@Path("address") String address);
@Headers("Content-Type: application/json")
@POST(ServerApiAdalite.ADALITE_UNSPENT_OUTPUTS)
Call<AdaliteResponseUtxo> adaliteUnspent(@Body String address);
// @GET(ServerApiAdalite.ADALITE_TRANSACTION)
// Call<AdaliteResponse> adaliteTransaction(@Path("txId") String txId);
@Headers("Content-Type: application/json")
@POST(ServerApiAdalite.ADALITE_SEND)
Call<String> adaliteSend(@Body AdaliteBody adaliteBody);
}

View file

@ -1,13 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.BinanceFees;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
public interface BinanceApi {
@GET("fees")
Call<List<BinanceFees>> binanceFees();
}

View file

@ -1,25 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.BlockchainInfoAddress;
import com.tangem.data.network.model.BlockchainInfoUnspents;
import io.reactivex.Single;
import okhttp3.ResponseBody;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface BlockchainInfoApi {
@GET(Server.ApiBlockchainInfo.Method.ADDRESS)
Single<BlockchainInfoAddress> blockchainInfoAddress(@Path("address") String address, @Query("offset") Integer offset);
@GET(Server.ApiBlockchainInfo.Method.UTXO)
Single<BlockchainInfoUnspents> blockchainInfoUnspents(@Query("active") String address);
@FormUrlEncoded
@POST(Server.ApiBlockchainInfo.Method.PUSH)
Single<ResponseBody> blockchainInfoPush(@Field("tx") String tx);
}

View file

@ -1,27 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.BlockchairAddressResponse;
import com.tangem.data.network.model.BlockchairSendBody;
import com.tangem.data.network.model.BlockchairStatsResponse;
import com.tangem.data.network.model.BlockchairTransactionResponse;
import io.reactivex.Completable;
import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface BlockchairApi {
@GET(Server.ApiBlockchair.Method.ADDRESS)
Single<BlockchairAddressResponse> getAddress(@Path("blockchain") String blockchain, @Path("address") String address);
@GET(Server.ApiBlockchair.Method.TRANSACTION)
Single<BlockchairTransactionResponse> getTransaction(@Path("blockchain") String blockchain, @Path("transaction") String transaction);
@GET(Server.ApiBlockchair.Method.STATS)
Single<BlockchairStatsResponse> getStats(@Path("blockchain") String blockchain);
@POST(Server.ApiBlockchair.Method.PUSH)
Completable sendTransaction(@Path("blockchain") String blockchain, @Body BlockchairSendBody body);
}

View file

@ -1,29 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.BlockcypherBody;
import com.tangem.data.network.model.BlockcypherResponse;
import com.tangem.data.network.model.BlockcypherFee;
import com.tangem.data.network.model.BlockcypherTx;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface BlockcypherApi {
@GET(Server.ApiBlockcypher.Method.MAIN)
Call<BlockcypherFee> blockcypherMain(@Path("blockchain") String blockchain, @Path("network") String network, @Query("token") String token);
@GET(Server.ApiBlockcypher.Method.ADDRESS)
Call<BlockcypherResponse> blockcypherAddress(@Path("blockchain") String blockchain, @Path("network") String network, @Path("address") String address, @Query("token") String token);
@GET(Server.ApiBlockcypher.Method.TXS)
Call<BlockcypherTx> blockcypherTxs(@Path("blockchain") String blockchain, @Path("network") String network, @Path("txHash") String txHash, @Query("token") String token);
@Headers("Content-Type: application/json")
@POST(Server.ApiBlockcypher.Method.PUSH)
Call<BlockcypherResponse> blockcypherPush(@Path("blockchain") String blockchain, @Path("network") String network, @Body BlockcypherBody blockcypherBody, @Query("token") String token);
}

View file

@ -1,7 +0,0 @@
package com.tangem.data.network
enum class BlockcypherToken(val token: String) {
T_001("aa8184b0e0894b88a5688e01b3dc1e82"),
T_002("56c4ca23c6484c8f8864c32fde4def8d"),
T_003("66a8a37c5e9d4d2c9bb191acfe7f93aa")
}

View file

@ -1,14 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.RateInfoResponse;
import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.Query;
public interface CoinmarketApi {
@Headers("X-CMC_PRO_API_KEY: f6622117-c043-47a0-8975-9d673ce484de")
@GET(Server.ApiCoinmarket.Method.PRICE_CONVERSION)
Observable<RateInfoResponse> getRateInfo(@Query("amount") int amount, @Query("symbol") String cryptoId);
}

View file

@ -1,317 +0,0 @@
package com.tangem.data.network;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.internal.LinkedTreeMap;
import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.wallet.R;
import java.io.IOException;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
//import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
/**
* HTTP
* Used in Cryptonit service
*/
public class Cryptonit {
private static final String SERVER_URL = "https://www.cryptonit.net:443/gateway/";
private static class Method {
public static final String AUTHENTICATE = SERVER_URL + "public/authenticate";
public static final String BALANCE = SERVER_URL + "private/balance";
public static final String WITHDRAW_COINS = SERVER_URL + "private/withdrawCoins";
}
public static class Model {
static class Authenticate
{
public static class Request
{
public String username;
public String password;
}
public static class Response
{
boolean success;
String[] missing_authenticators;
public Object[] infos;
public Object[] warnings;
public Object[] errors;
AuthenticationResult results;
}
static class AuthenticationResult {
// token (string): authentication token,
// nick (string),
// stayLoggedIn (boolean),
// integer lastLogin (integer): JavaScript time OR NEVER for first login,
// preferredLanguage (string) = ['en' or 'de']
}
}
public static class Balance {
static class Request {
String[] currencies;
}
public static class Response {
public boolean success;
public Object[] infos;
public Object[] warnings;
public Object[] errors;
public BalanceResult[] results;
public static class BalanceResult {
public String currency;
public double balance;
public String receiveAddress; //the current address to receive funds for this account, if available,
public boolean fiat; // Is this a FIAT currency? If false, this is a CRYPTO currency.,
public Object[] unprocessedTransactions; // (array, optional): list of unprocessed transactions, if pass field withTransactions
}
}
}
public static class WithdrawCoins {
public static class Request {
public String currency;
public Double amount;
String toAddress;
Double includeMinerFee;
String password;
}
public static class Response {
public Boolean success;
public String[] missing_authenticators;
public Object[] infos;
public Object[] warnings;
public Object[] errors;
}
}
}
public interface Api {
@Headers("Content-Type: application/json")
@POST(Method.AUTHENTICATE)
Call<Model.Authenticate.Response> authenticate(@Body Model.Authenticate.Request request);
@Headers("Content-Type: application/json")
@POST(Method.BALANCE)
Observable<Model.Balance.Response> getBalance(@Header("Auth-Token") String authToken, @Body Model.Balance.Request request);
@Headers("Content-Type: application/json")
@POST(Method.WITHDRAW_COINS)
Observable<Model.WithdrawCoins.Response> withdrawCoins(@Header("Auth-Token") String authToken, @Body Model.WithdrawCoins.Request request);
}
private Api api = null;
private BalanceListener balanceListener;
private WithdrawalListener withdrawalListener;
private ErrorListener errorListener;
private Context context;
private String authToken;
public Cryptonit(Context context) {
this.context = context;
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
username = sp.getString(context.getResources().getString(R.string.key_cryptonit_username), "");
password = sp.getString(context.getResources().getString(R.string.key_cryptonit_password), "");
fee = sp.getString(context.getResources().getString(R.string.key_cryptonit_fee), "0.0");
}
public String username;
public String password;
private String fee;
public String getFee() {
return fee;
}
public void setFee(String value)
{
fee=value;
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit()
.putString(context.getResources().getString(R.string.key_cryptonit_fee), fee)
.apply();
}
public Boolean haveAccountInfo() {
return !username.isEmpty() && !password.isEmpty();
}
public void saveAccountInfo() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit()
.putString(context.getResources().getString(R.string.key_cryptonit_username), username)
.putString(context.getResources().getString(R.string.key_cryptonit_password), password)
.apply();
}
public interface BalanceListener {
void onBalanceData(Model.Balance.Response response);
}
public interface WithdrawalListener {
void onWithdrawalComplete(Model.WithdrawCoins.Response response);
}
public interface ErrorListener {
void onError(Throwable throwable);
}
public void setBalanceListener(BalanceListener listener) {
balanceListener = listener;
}
public void setWithdrawalListener(WithdrawalListener listener) {
withdrawalListener = listener;
}
public void setErrorListener(ErrorListener listener) {
errorListener = listener;
}
@SuppressLint("CheckResult")
public void requestBalance(String currency) {
initApi();
// Log.e("CRYPTONIT2", "username: " + username);
// Log.e("CRYPTONIT2", "password: " + password);
// Log.e("CRYPTONIT2", "auth-token: " + authToken);
Model.Balance.Request request=new Model.Balance.Request();
request.currencies=new String[] {currency};
api.getBalance(authToken, request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(balanceModel -> balanceListener.onBalanceData(balanceModel),
// handle error
this::FireError
);
}
@SuppressLint("CheckResult")
public void requestWithdrawCoins(String currency, Double amount, String address) {
initApi();
// Log.e("CRYPTONIT2", "username: " + username);
// Log.e("CRYPTONIT2", "password: " + password);
// Log.e("CRYPTONIT2", "auth-token: " + authToken);
Model.WithdrawCoins.Request request=new Model.WithdrawCoins.Request();
request.currency=currency;
request.amount=amount;
request.toAddress=address;
request.includeMinerFee=Double.parseDouble(fee);
request.password=password;
api.withdrawCoins(authToken, request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {
if (response.success != null && response.success)
withdrawalListener.onWithdrawalComplete(response);
else {
errorListener.onError(new Exception(((LinkedTreeMap) response.errors[0]).entrySet().toArray()[0].toString()));
}
},
// handle error
this::FireError
);
}
private void FireError(Throwable e) throws IOException {
if (e.getClass() == HttpException.class && ((HttpException) e).code() == 500) {
JsonObject jsonObject = new JsonParser().parse(((HttpException) e).response().errorBody().string()).getAsJsonObject();
errorListener.onError(new Exception(e.getMessage() + ": " + jsonObject.get("errors").getAsString()));
// if (jsonObject.get("reason").getAsString().equals("Invalid nonce")) nonce += 1000;
} else {
errorListener.onError(e);
}
}
private void initApi() {
if (api != null) return;
// HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
// logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient.Builder().
// addInterceptor(logging).
addInterceptor(new AuthorizationInterceptor()).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpClient)
.build();
api = retrofit.create(Api.class);
}
public class AuthorizationInterceptor implements Interceptor {
AuthorizationInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Request mainRequest=chain.request();
if( !mainRequest.url().toString().endsWith("authenticate")&& authToken==null )
{
Model.Authenticate.Request authRequest=new Model.Authenticate.Request();
authRequest.username=username;
authRequest.password=password;
retrofit2.Response<Model.Authenticate.Response> authResponse=api.authenticate(authRequest).execute();
if( authResponse.isSuccessful() && authResponse.body().success)
{
String newToken = authResponse.headers().get("auth-token");
if (newToken != null) {
authToken = newToken;
}
}else{
throw new IOException("Authentication error: "+authResponse.message());
}
}
if( authToken!=null ) {
mainRequest = mainRequest.newBuilder().addHeader("auth-token", authToken).build();
}
Response mainResponse = chain.proceed(mainRequest);
if (!mainResponse.isSuccessful()) {
authToken=null;
}
return mainResponse;
}
}
}

View file

@ -1,283 +0,0 @@
package com.tangem.data.network;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.annotations.SerializedName;
import com.google.gson.internal.LinkedTreeMap;
import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.tangem_card.util.Util;
import com.tangem.wallet.R;
import java.io.IOException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Headers;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Path;
//import okhttp3.logging.HttpLoggingInterceptor;
/**
* HTTP
* Used in CryptonitOtherApi service
*/
public class CryptonitOtherApi {
private static final String SERVER_URL = "https://api.cryptonit.net/api/";
private static class Method {
public static final String BALANCE = SERVER_URL + "balance/{cryptoCurrency}%2F{fiatCurrency}";
public static final String CRYPTO_WITHDRAWAL = SERVER_URL + "crypto_withdrawal/";
}
public static class Response {
public static class Balance{
@SerializedName("btc_balance")
public String btc_balance;
@SerializedName("eur_balance")
public String eur_balance;
@SerializedName("eth_balance")
public String eth_balance;
@SerializedName("etn_balance")
public String etn_balance;
@SerializedName("btc_reserved")
public String btc_reserved;
@SerializedName("eur_reserved")
public String eur_reserved;
@SerializedName("eth_reserved")
public String eth_reserved;
@SerializedName("etn_reserved")
public String etn_reserved;
@SerializedName("btc_available")
public String btc_available;
@SerializedName("eur_available")
public String eur_available;
@SerializedName("eth_available")
public String eth_available;
@SerializedName("etn_available")
public String etn_available;
@SerializedName("btceur_fee")
public String btceur_fee;
@SerializedName("etheur_fee")
public String etheur_fee;
@SerializedName("etnbtc_fee")
public String etnbtc_fee;
@SerializedName("fee")
public String fee;
}
public static class CryptoWithdrawal {
@SerializedName("success")
public Boolean success;
@SerializedName("status")
public String status;
@SerializedName("reason")
public Object reason;
}
}
public interface Api {
@Multipart
@Headers("accept: multipart/form-data")
@POST(Method.BALANCE)
Observable<Response.Balance> getBalance(
@Path("cryptoCurrency") String cryptoCurrency, @Path("fiatCurrency") String fiatCurrency,
@Part(value = "key") RequestBody key, @Part("signature") RequestBody signature, @Part("nonce") RequestBody nonce);
@Multipart
@Headers("accept: multipart/form-data")
@POST(Method.CRYPTO_WITHDRAWAL)
Observable<Response.CryptoWithdrawal> cryptoWithdrawal(
@Part(value = "currency") RequestBody currency, @Part("amount") RequestBody amount, @Part("address") RequestBody address,
@Part(value = "key") RequestBody key, @Part("signature") RequestBody signature, @Part("nonce") RequestBody nonce);
}
private Api api = null;
private BalanceListener balanceListener;
private WithdrawalListener withdrawalListener;
private ErrorListener errorListener;
private Context context;
public CryptonitOtherApi(Context context) {
this.context = context;
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
key = sp.getString(context.getResources().getString(R.string.key_cryptonit_key), "");
userId = sp.getString(context.getResources().getString(R.string.key_cryptonit_user_id), "");
secret = sp.getString(context.getResources().getString(R.string.key_cryptonit_secret), "");
nonce = sp.getInt(context.getResources().getString(R.string.key_cryptonit_nonce), 0);
}
public String key;
public String userId;
public String secret;
private Integer nonce;
public String getSecretDescription() {
if (secret == null || secret.isEmpty()) return "";
return secret.substring(0, 3) + "..." + secret.substring(secret.length() - 3, secret.length());
}
public Boolean havaAccountInfo() {
return (!userId.isEmpty() && !key.isEmpty() && !secret.isEmpty());
}
public void saveAccountInfo() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
nonce = 1;
sp.edit()
.putString(context.getResources().getString(R.string.key_cryptonit_key), key)
.putString(context.getResources().getString(R.string.key_cryptonit_user_id), userId)
.putString(context.getResources().getString(R.string.key_cryptonit_secret), secret)
.putInt(context.getResources().getString(R.string.key_cryptonit_nonce), nonce)
.apply();
}
private void incNonce() {
nonce++;
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit().putInt(context.getResources().getString(R.string.key_cryptonit_nonce), nonce).apply();
}
public interface BalanceListener {
void onBalanceData(Response.Balance response);
}
public interface WithdrawalListener {
void onWithdrawalComplete(Response.CryptoWithdrawal response);
}
public interface ErrorListener {
void onError(Throwable throwable);
}
public void setBalanceListener(BalanceListener listener) {
balanceListener = listener;
}
public void setWithdrawalListener(WithdrawalListener listener) {
withdrawalListener = listener;
}
public void setErrorListener(ErrorListener listener) {
errorListener = listener;
}
private String calcSignature() throws Exception {
incNonce();
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
String data = nonce.toString() + userId + key;
return Util.bytesToHex(sha256_HMAC.doFinal(data.getBytes("UTF-8")));
}
@SuppressLint("CheckResult")
public void requestBalance(String cryptoCurrency, String fiatCurrency) throws Exception {
initApi();
String signature = calcSignature();
// Log.e("CRYPTONIT", "user: " + userId);
// Log.e("CRYPTONIT", "key: " + key);
// Log.e("CRYPTONIT", "secret: " + secret);
// Log.e("CRYPTONIT", "nonce: " + nonce);
// Log.e("CRYPTONIT", "signature: " + signature);
api.getBalance(cryptoCurrency, fiatCurrency,
RequestBody.create(MediaType.parse("text/plain"), key),
RequestBody.create(MediaType.parse("text/plain"), signature),
RequestBody.create(MediaType.parse("text/plain"), nonce.toString()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(balanceModel -> balanceListener.onBalanceData(balanceModel),
// handle error
this::FireError
);
}
@SuppressLint("CheckResult")
public void requestCryptoWithdrawal(String currency, String amount, String address) throws Exception {
initApi();
String signature = calcSignature();
// Log.e("CRYPTONIT", "user: " + userId);
// Log.e("CRYPTONIT", "key: " + key);
// Log.e("CRYPTONIT", "secret: " + secret);
// Log.e("CRYPTONIT", "nonce: " + nonce);
// Log.e("CRYPTONIT", "signature: " + signature);
//
api.cryptoWithdrawal(
RequestBody.create(MediaType.parse("text/plain"), currency),
RequestBody.create(MediaType.parse("text/plain"), amount),
RequestBody.create(MediaType.parse("text/plain"), address),
RequestBody.create(MediaType.parse("text/plain"), key),
RequestBody.create(MediaType.parse("text/plain"), signature),
RequestBody.create(MediaType.parse("text/plain"), nonce.toString()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {
if (response.success != null && response.success)
withdrawalListener.onWithdrawalComplete(response);
else {
LinkedTreeMap reason = (LinkedTreeMap) response.reason;
errorListener.onError(new Exception(reason.entrySet().toArray()[0].toString()));
}
},
// handle error
this::FireError
);
}
private void FireError(Throwable e) throws IOException {
if (e.getClass() == HttpException.class && ((HttpException) e).code() == 500) {
JsonObject jsonObject = new JsonParser().parse(((HttpException) e).response().errorBody().string()).getAsJsonObject();
errorListener.onError(new Exception(e.getMessage() + ": " + jsonObject.get("reason").getAsString()));
if (jsonObject.get("reason").getAsString().equals("Invalid nonce")) nonce += 1000;
} else {
errorListener.onError(e);
}
}
private void initApi() {
if (api != null) return;
// HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
// logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient.Builder().
// addInterceptor(logging).
build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpClient)
.build();
api = retrofit.create(Api.class);
}
}

View file

@ -1,25 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.BitcoreBalance;
import com.tangem.data.network.model.BitcoreSendBody;
import com.tangem.data.network.model.BitcoreSendResponse;
import com.tangem.data.network.model.BitcoreUtxo;
import java.util.List;
import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface DucatusApi {
@GET(Server.ApiDucatus.Method.BALANCE)
Single<BitcoreBalance> ducatusBalance(@Path("address") String address);
@GET(Server.ApiDucatus.Method.UTXO)
Single<List<BitcoreUtxo>> ducatusUnspents(@Path("address") String address);
@POST(Server.ApiDucatus.Method.SEND)
Single<BitcoreSendResponse> ducatusSend(@Body BitcoreSendBody body);
}

View file

@ -1,193 +0,0 @@
package com.tangem.data.network;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Created by dvol on 16.07.2017.
*/
public class ElectrumRequest {
public static final String METHOD_GetBalance = "blockchain.address.get_balance";
public static final String METHOD_ListUnspent = "blockchain.address.listunspent";
public static final String METHOD_GetHistory = "blockchain.address.get_history";
public static final String METHOD_GetTransaction = "blockchain.transaction.get";
public static final String METHOD_GetHeader = "blockchain.block.get_header";
public static final String METHOD_SendTransaction = "blockchain.transaction.broadcast";
public static final String METHOD_GetFee = "blockchain.estimatefee";
private JSONObject jsRequestData;
String answerData;
private String error = null;
private String walletAddress;
public String txHash;
private String TX;
String host;
int port;
private ElectrumRequest() {
}
public ElectrumRequest(JSONObject jsRequest) {
try {
jsRequestData = new JSONObject(jsRequest.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
public JSONObject getAnswer() {
try {
return new JSONObject(answerData);
} catch (Exception e) {
try {
return new JSONObject(String.format("[\"Error\":\"%s\"]", e.getMessage()));
} catch (JSONException e1) {
e1.printStackTrace();
return null;
}
}
}
public String getAsString() {
return jsRequestData.toString();
}
public void setID(int value) {
try {
jsRequestData.put("id", String.format("%d", value));
} catch (JSONException e) {
e.printStackTrace();
}
}
public int getID() {
try {
return jsRequestData.getInt("id");
} catch (JSONException e) {
e.printStackTrace();
return 0;
}
}
public static ElectrumRequest checkBalance(String wallet) {
ElectrumRequest request = new ElectrumRequest();
try {
request.walletAddress = wallet;
request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetBalance + "\", \"params\":[\"" + wallet + "\"] }");
} catch (JSONException e) {
e.printStackTrace();
request.error = e.toString();
}
return request;
}
public static ElectrumRequest getFee() {
ElectrumRequest request = new ElectrumRequest();
try {
request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetFee + "\", \"params\":[\"" + 6 + "\"] }");
} catch (JSONException e) {
e.printStackTrace();
request.error = e.toString();
}
return request;
}
public static ElectrumRequest listUnspent(String wallet) {
ElectrumRequest request = new ElectrumRequest();
try {
request.walletAddress = wallet;
request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ListUnspent + "\", \"params\":[\"" + wallet + "\"] }");
} catch (JSONException e) {
e.printStackTrace();
request.error = e.toString();
}
return request;
}
public static ElectrumRequest broadcast(String wallet, String tx) {
ElectrumRequest request = new ElectrumRequest();
try {
request.walletAddress = wallet;
request.TX = tx;
request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_SendTransaction + "\", \"params\":[\"" + tx + "\"] }");
} catch (JSONException e) {
e.printStackTrace();
request.error = e.toString();
}
return request;
}
public static ElectrumRequest getTransaction(String wallet, String tx_hash) {
ElectrumRequest request = new ElectrumRequest();
try {
request.walletAddress = wallet;
request.txHash = tx_hash;
request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetTransaction + "\", \"params\":[\"" + tx_hash + "\"] }");
} catch (JSONException e) {
e.printStackTrace();
request.error = e.toString();
}
return request;
}
public String getMethod() {
try {
return jsRequestData.getString("method");
} catch (JSONException e) {
e.printStackTrace();
}
return "";
}
public boolean isMethod(String methodName) {
return getMethod().equals(methodName);
}
public JSONArray getParams() throws JSONException {
return jsRequestData.getJSONArray("params");
}
public JSONObject getResult() throws JSONException {
return getAnswer().getJSONObject("result");
}
public String getError() {
if( answerData!=null ) {
// answer received - return error from it
JSONObject answer = getAnswer();
if (answer.has("error")) {
try {
return getAnswer().getJSONObject("error").toString();
} catch (JSONException e) {
e.printStackTrace();
return null;
}
} else {
return null;
}
}else{
// no answer received - return saved error reason
return error;
}
}
public void setError(String error) {
this.error = error;
}
public String getResultString() throws JSONException {
if (getAnswer().has("result")) {
return getAnswer().getString("result");
} else {
return null;
}
}
public JSONArray getResultArray() throws JSONException {
return getAnswer().getJSONArray("result");
}
}

View file

@ -1,15 +0,0 @@
package com.tangem.data.network;
import retrofit2.Call;
import retrofit2.http.GET;
public interface EstimatefeeApi {
@GET(Server.ApiEstimatefee.Method.N_2)
Call<String> getEstimateFeePriority();
@GET(Server.ApiEstimatefee.Method.N_3)
Call<String> getEstimateFeeNormal();
@GET(Server.ApiEstimatefee.Method.N_6)
Call<String> getEstimateFeeMinimal();
}

View file

@ -1,15 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface InfuraApi {
@Headers("Content-Type: application/json")
@POST(Server.ApiInfura.Method.MAIN)
Call<InfuraResponse> infura(@Body InfuraBody body);
}

View file

@ -1,33 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.InsightBody;
import com.tangem.data.network.model.InsightResponse;
import com.tangem.data.network.model.InsightUtxo;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface InsightApi {
@GET(ServerApiInsight.INSIGHT_ADDRESS)
Call<InsightResponse> insightAddress(@Path("address") String address);
@GET(ServerApiInsight.INSIGHT_UNSPENT_OUTPUTS)
Call<List<InsightUtxo>> insightUnspent(@Path("address") String address);
@GET(ServerApiInsight.INSIGHT_TRANSACTION)
Call<InsightResponse> insightTransaction(@Path("txId") String txId);
@GET(ServerApiInsight.INSIGHT_FEE)
Call<InsightResponse> insightFee();
@Headers("Content-Type: application/json")
@POST(ServerApiInsight.INSIGHT_SEND)
Call<InsightResponse> insightSend(@Body InsightBody body );
}

View file

@ -1,358 +0,0 @@
package com.tangem.data.network;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.tangem_card.util.Util;
import com.tangem.wallet.R;
import org.spongycastle.util.encoders.Base64;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.Buffer;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
//import okhttp3.logging.HttpLoggingInterceptor;
/**
* HTTP
* Used in Kraken service
*/
public class Kraken {
private static final String SERVER_URL = "https://api.kraken.com";
private static class Method {
public static final String BALANCE = SERVER_URL + "/0/private/Balance";
public static final String WITHDRAW_INFO = SERVER_URL + "/0/private/WithdrawInfo";
public static final String WITHDRAW = SERVER_URL + "/0/private/Withdraw";
}
public static class Model {
public static class Balance {
public static class Response {
public String[] error;
public Result result;
public static class Result {
public String XXBT;
public String XETH;
public String BCH;
}
}
}
public static class WithdrawInfo {
public static class Response {
public String[] error;
public Result result;
public static class Result {
public String fee;
public String amount;
}
}
}
public static class Withdraw {
public static class Response {
public String[] error;
public Result result;
public static class Result {
public String refid;
}
}
}
}
public interface Api {
@FormUrlEncoded
@POST(Method.BALANCE)
Observable<Model.Balance.Response> getBalance(@Field("nonce") String nonce);
@FormUrlEncoded
@POST(Method.WITHDRAW_INFO)
Observable<Model.WithdrawInfo.Response> WithdrawInfo(@Field("nonce") String nonce, @Field("asset") String asset, @Field("key") String key, @Field("amount") String amount);
@FormUrlEncoded
@POST(Method.WITHDRAW)
Observable<Model.Withdraw.Response> Withdraw(@Field("nonce") String nonce, @Field("asset") String asset, @Field("key") String key, @Field("amount") String amount);
}
private Api api = null;
private BalanceListener balanceListener;
private WithdrawalListener withdrawalListener;
private WithdrawalInfoListener withdrawalInfoListener;
private ErrorListener errorListener;
private Context context;
public Kraken(Context context) {
this.context = context;
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
key = sp.getString(context.getResources().getString(R.string.key_kraken_key), "");
secret = sp.getString(context.getResources().getString(R.string.key_kraken_secret), "");
nonce = sp.getInt(context.getResources().getString(R.string.key_kraken_nonce), 0);
}
public String key;
public String secret;
private Integer nonce;
public String getSecretDescription() {
if (secret == null || secret.isEmpty()) return "";
return secret.substring(0, 3) + "..." + secret.substring(secret.length() - 3, secret.length());
}
public Boolean haveAccountInfo() {
return (!key.isEmpty() && !secret.isEmpty());
}
public void saveAccountInfo() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit()
.putString(context.getResources().getString(R.string.key_kraken_key), key)
.putString(context.getResources().getString(R.string.key_kraken_secret), secret)
.putInt(context.getResources().getString(R.string.key_kraken_nonce), nonce)
.apply();
}
private void incNonce() {
nonce++;
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit().putInt(context.getResources().getString(R.string.key_kraken_nonce), nonce).apply();
}
public interface BalanceListener {
void onBalanceData(Model.Balance.Response response);
}
public interface WithdrawalInfoListener {
void onWithdrawalInfoComplete(Model.WithdrawInfo.Response response);
}
public interface WithdrawalListener {
void onWithdrawalComplete(Model.Withdraw.Response response);
}
public interface ErrorListener {
void onError(Throwable throwable);
}
public void setBalanceListener(BalanceListener listener) {
balanceListener = listener;
}
public void setWithdrawalListener(WithdrawalListener listener) {
withdrawalListener = listener;
}
public void setWithdrawalInfoListener(WithdrawalInfoListener listener) {
withdrawalInfoListener = listener;
}
public void setErrorListener(ErrorListener listener) {
errorListener = listener;
}
private static final String HMAC_SHA512 = "HmacSHA512";
private String bodyToString(final RequestBody request){
try {
final RequestBody copy = request;
final Buffer buffer = new Buffer();
if(copy != null)
copy.writeTo(buffer);
else
return "";
return buffer.readUtf8();
}
catch (final IOException e) {
return "did not work";
}
}
private String calcSignature(String url, RequestBody requestBody) throws NoSuchAlgorithmException, InvalidKeyException, IOException {
String postData=bodyToString(requestBody);
// create SHA-256 hash of the nonce and the POST data
String s=nonce+postData;
byte[] sha256 = Util.calculateSHA256(s);
// set the API method and retrieve the path
byte[] path = url.getBytes(StandardCharsets.UTF_8);
// decode the API secret, it's the HMAC key
byte[] hmacKey = Base64.decode(secret);
// create the HMAC message from the path and the previous hash
ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
outputStream.write(path);
outputStream.write(sha256);
byte[] hmacMessage = outputStream.toByteArray();//concatArrays(path, sha256);
Mac mac = Mac.getInstance(HMAC_SHA512);
mac.init(new SecretKeySpec(hmacKey, HMAC_SHA512));
byte[] hmacSignature=mac.doFinal(hmacMessage);
byte[] b64Signature = Base64.encode(hmacSignature);
return new String(b64Signature, StandardCharsets.UTF_8);
}
@SuppressLint("CheckResult")
public void requestBalance() throws Exception {
initApi();
// Log.e("kraken", "key: " + key);
// Log.e("kraken", "secret: " + secret);
// Log.e("kraken", "nonce: " + nonce);
api.getBalance(nonce.toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> balanceListener.onBalanceData(response),
// handle error
this::FireError
);
}
@SuppressLint("CheckResult")
public void requestWithdrawInfo(String currency, String amount, String withdrawKey) throws Exception {
initApi();
// Log.e("kraken", "key: " + key);
// Log.e("kraken", "secret: " + secret);
// Log.e("kraken", "nonce: " + nonce);
String asset=CurrencyToAsset(currency);
// withdrawKey = "test";
api.WithdrawInfo(nonce.toString(), asset, withdrawKey, amount)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> withdrawalInfoListener.onWithdrawalInfoComplete(response),
// handle error
this::FireError
);
}
private static String CurrencyToAsset(String currency) throws Exception {
switch (currency)
{
case "BTC": return "XXBT";
case "BCH": return "BCH";
case "ETH": return "XETH";
default:
throw new Exception("Unsupported currency!");
}
}
@SuppressLint("CheckResult")
public void requestWithdraw(String currency, String amount, String withdrawKey) throws Exception {
initApi();
// Log.e("kraken", "key: " + key);
// Log.e("kraken", "secret: " + secret);
// Log.e("kraken", "nonce: " + nonce);
String asset=CurrencyToAsset(currency);
// withdrawKey = "test";
api.Withdraw(nonce.toString(), asset, withdrawKey, amount)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> withdrawalListener.onWithdrawalComplete(response),
// handle error
this::FireError
);
}
private void FireError(Throwable e) throws IOException {
// if (e.getClass() == HttpException.class && ((HttpException) e).code() == 500) {
// JsonObject jsonObject = new JsonParser().parse(((HttpException) e).response().errorBody().string()).getAsJsonObject();
// errorListener.onError(new Exception(e.getMessage() + ": " + jsonObject.get("reason").getAsString()));
// if (jsonObject.get("reason").getAsString().equals("Invalid nonce")) nonce += 1000;
// } else {
errorListener.onError(e);
// }
}
private void initApi() {
if (api != null) return;
// HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
// logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient.Builder().
addInterceptor(new AuthorizationInterceptor()).
// addInterceptor(logging).
build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpClient)
.build();
api = retrofit.create(Api.class);
}
public class AuthorizationInterceptor implements Interceptor {
AuthorizationInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Request mainRequest=chain.request();
try {
mainRequest = mainRequest.newBuilder().
addHeader("API-Key", key).
addHeader("API-Sign", calcSignature(mainRequest.url().toString().substring(SERVER_URL.length()), mainRequest.body())).
build();
incNonce();
} catch (Exception e) {
e.printStackTrace();
throw new IOException("Can't calculate signature: "+e.getMessage());
}
return chain.proceed(mainRequest);
}
}
}

View file

@ -1,15 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface MaticApi {
@Headers("Content-Type: application/json")
@POST(Server.ApiMaticTesnet.Method.MAIN)
Call<InfuraResponse> matic(@Body InfuraBody body);
}

View file

@ -1,15 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.PayIdResponse;
import io.reactivex.Single;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.Path;
public interface PayIdApi {
@Headers("PayID-Version: 1.0")
@GET("{user}")
Single<PayIdResponse> getAddress(@Path("user") String user, @Header("Accept") String acceptHeader);
}

View file

@ -1,15 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.RippleBody;
import com.tangem.data.network.model.RippleResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface RippleApi {
@Headers("Content-Type: application/json")
@POST("./")
Call<RippleResponse> ripple(@Body RippleBody body);
}

View file

@ -1,15 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface RootstockApi {
@Headers("Content-Type: application/json")
@POST(Server.ApiRootstock.Method.MAIN)
Call<InfuraResponse> rootstock(@Body InfuraBody body);
}

View file

@ -1,170 +0,0 @@
package com.tangem.data.network;
public class Server {
public static class ApiUpdateVersion {
public static final String URL_UPDATE_VERSION = ServerURL.API_UPDATE_VERSION;
public static class Method {
static final String LAST_VERSION = URL_UPDATE_VERSION + "TangemCash/tangem-binaries/master/apk-version.txt";
}
}
/**
* https://coinmarketcap.com/api/
*/
public static class ApiCoinmarket {
public static final String URL_COINMARKET = ServerURL.API_COINMARKETCAP;
public static class Method {
static final String PRICE_CONVERSION = URL_COINMARKET + "v1/tools/price-conversion";
}
}
/**
* https://infura.io/
*/
public static class ApiInfura {
public static final String URL_INFURA = ServerURL.API_INFURA;
public static class Method {
public static final String MAIN = "v3/613a0b14833145968b1f656240c7d245";
}
}
public static class ApiInfuraTestnet {
public static final String URL_INFURA_TESTNET = ServerURL.API_INFURA_TESTNET;
public static class Method {
public static final String MAIN = "v3/613a0b14833145968b1f656240c7d245";
}
}
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;
public static class Method {
public static final String ADDRESS_BALANCE = "api/v2/get_address_balance/{network}/{address}";
public static final String UNSPENT_TX = "api/v2/get_tx_unspent/{network}/{address}";
public static final String GET_TX = "api/v2/get_tx/{network}/{txid}";
public static final String SEND_TRANSACTION = "api/v2/send_tx/{network}";
}
}
/**
* https://public-node.rsk.co/
*/
public static class ApiRootstock {
public static final String URL_ROOTSTOCK = ServerURL.API_ROOTSTOCK;
public static class Method {
static final String MAIN = URL_ROOTSTOCK;
}
}
/**
* https://testnet2.matic.network
*/
public static class ApiMaticTesnet {
public static final String URL_MATIC_TESTNET = ServerURL.API_MATIC_TESTNET ;
public static class Method {
static final String MAIN = URL_MATIC_TESTNET;
}
}
/**
* https://estimatefee.com/
*/
public static class ApiEstimatefee {
public static final String URL_ESTIMATEFEE = ServerURL.API_ESTIMATEFEE;
public static class Method {
static final String N_2 = URL_ESTIMATEFEE + "n/2";
static final String N_3 = URL_ESTIMATEFEE + "n/3";
static final String N_6 = URL_ESTIMATEFEE + "n/6";
}
}
/**
* https://dex.binance.org/
*/
public static class ApiBinance {
public static final String URL_BINANCE = ServerURL.API_BINANCE;
public static class Method {
public static final String API_V1 = URL_BINANCE + "api/v1/";
}
}
/**
* https://testnet-dex.binance.org/
*/
public static class ApiBinanceTestnet {
public static final String URL_BINANCE_TESTNET = ServerURL.API_BINANCE_TESTNET;
public static class Method {
public static final String API_V1 = URL_BINANCE_TESTNET + "api/v1/";
}
}
/**
* https://api.blockcypher.com/
*/
public static class ApiBlockcypher {
public static final String URL_BLOCKCYPHER = ServerURL.API_BLOCKCYPHER;
static final String V1_MAIN = "v1/{blockchain}/{network}";
public static class Method {
static final String MAIN = URL_BLOCKCYPHER + V1_MAIN;
static final String ADDRESS = MAIN + "/addrs/{address}?includeScript=true&limit=2000";
static final String TXS = MAIN + "/txs/{txHash}?includeHex=true";
static final String PUSH = MAIN + "/txs/push";
}
}
public static class ApiBlockchainInfo {
public static final String URL_BLOCKCHAININFO = ServerURL.API_BLOCKCHAIN_INFO;
public static class Method {
static final String ADDRESS = URL_BLOCKCHAININFO + "rawaddr/{address}";
static final String UTXO = URL_BLOCKCHAININFO + "unspent";
// static final String TX = URL_BLOCKCHAININFO + "rawtx/{txHash}";
static final String PUSH = URL_BLOCKCHAININFO + "pushtx";
}
}
public static class ApiDucatus {
public static final String URL_DUCATUS = ServerURL.API_DUCATUS + "api/DUC/mainnet/";
public static class Method {
static final String BALANCE = URL_DUCATUS + "address/{address}/balance";
static final String UTXO = URL_DUCATUS + "address/{address}/?unspent=true";
static final String SEND = URL_DUCATUS + "tx/send";
}
}
public static class ApiBlockchair {
public static final String URL_BLOCKCHAIR = ServerURL.API_BLOCKCHAIR + "{blockchain}/";
private static final String API_KEY = "?key=A___0Shpsu4KagE7oSabrw20DfXAqWlT";
public static class Method {
static final String ADDRESS = URL_BLOCKCHAIR + "dashboards/address/{address}" + API_KEY;
static final String TRANSACTION = URL_BLOCKCHAIR + "dashboards/transaction/{transaction}" + API_KEY;
static final String STATS = URL_BLOCKCHAIR + "stats" + API_KEY;
static final String PUSH = URL_BLOCKCHAIR + "push/transaction" + API_KEY;
}
}
}

View file

@ -1,196 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.data.network.model.AdaliteBody;
import com.tangem.data.network.model.AdaliteResponse;
import com.tangem.data.network.model.AdaliteResponseUtxo;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class ServerApiAdalite {
private static String TAG = ServerApiAdalite.class.getSimpleName();
public static final String ADALITE_ADDRESS = "/api/addresses/summary/{address}";
public static final String ADALITE_UNSPENT_OUTPUTS = "/api/bulk/addresses/utxo";
// public static final String ADALITE_TRANSACTION = "/api/txs/raw/{txId}}";
public static final String ADALITE_SEND = "/api/v2/txs/signed";
private int requestsCount = 0;
private final String adaliteURL1 = "https://explorer2.adalite.io"; //TODO: make random selection, add more?, move
private final String adaliteURL2 = "https://nodes.southeastasia.cloudapp.azure.com";
private String currentURL = adaliteURL1;
public String getCurrentURL() {
return currentURL;
}
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, AdaliteResponse adaliteResponse);
void onSuccess(String method, AdaliteResponseUtxo adaliteResponseUtxo);
void onSuccess(String method, String stringResponse);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, String wallet, String tx) {
requestData(method, wallet, tx, false);
}
public void requestData(String method, String wallet, String tx, boolean isRetry) {
requestsCount++;
Retrofit retrofitAdalite = new Retrofit.Builder()
.baseUrl(currentURL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
AdaliteApi adaliteApi = retrofitAdalite.create(AdaliteApi.class);
switch (method) {
case ADALITE_ADDRESS:
Call<AdaliteResponse> addressCall = adaliteApi.adaliteAddress(wallet);
addressCall.enqueue(new Callback<AdaliteResponse>() {
@Override
public void onResponse(@NonNull Call<AdaliteResponse> call, @NonNull Response<AdaliteResponse> response) {
requestsCount--;
if (response.code() == 200) {
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(response.code()));
}
}
}
@Override
public void onFailure(@NonNull Call<AdaliteResponse> call, @NonNull Throwable t) {
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
requestsCount--;
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(t.getMessage()));
}
}
});
break;
case ADALITE_UNSPENT_OUTPUTS:
Call<AdaliteResponseUtxo> outputsCall = adaliteApi.adaliteUnspent("[\"" + wallet + "\"]");
outputsCall.enqueue(new Callback<AdaliteResponseUtxo>() {
@Override
public void onResponse(@NonNull Call<AdaliteResponseUtxo> call, @NonNull Response<AdaliteResponseUtxo> response) {
requestsCount--;
if (response.code() == 200) {
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(response.code()));
}
}
}
@Override
public void onFailure(@NonNull Call<AdaliteResponseUtxo> call, @NonNull Throwable t) {
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
requestsCount--;
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(t.getMessage()));
}
}
});
break;
case ADALITE_SEND:
Call<String> sendCall = adaliteApi.adaliteSend(new AdaliteBody(tx));
sendCall.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
requestsCount--;
if (response.code() == 200) {
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(response.code()));
}
}
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
requestsCount--;
if (!isRetry) {
retryRequest(method, wallet, tx);
} else {
responseListener.onFail(method, String.valueOf(t.getMessage()));
}
}
});
break;
default:
requestsCount--;
responseListener.onFail(method, "undeclared method");
Log.e(TAG, "requestData " + method + " onFailure - undeclared method");
break;
}
}
private void retryRequest(String method, String wallet, String tx) {
// currentURL = adaliteURL2;
requestData(method, wallet, tx, true);
}
}

View file

@ -1,137 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import com.tangem.wallet.TangemContext;
import com.tangem.wallet.Transaction;
import com.tangem.wallet.binance.BinanceAssetData;
import com.tangem.wallet.binance.BinanceData;
import com.tangem.wallet.binance.client.BinanceDexApiRestClient;
import com.tangem.wallet.binance.client.domain.Account;
import com.tangem.wallet.binance.client.domain.Balance;
import com.tangem.wallet.binance.client.domain.TransactionMetadata;
import com.tangem.wallet.binance.client.encoding.message.TransactionRequestAssemblerExtSign;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DefaultObserver;
import io.reactivex.schedulers.Schedulers;
import okhttp3.RequestBody;
public class ServerApiBinance {
private static String TAG = ServerApiBinance.class.getSimpleName();
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess();
void onFail();
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void getBalance(TangemContext ctx, BinanceDexApiRestClient client) {
Log.i(TAG, "new getBalance request");
Observable<Account> balanceObservable = Observable.just(new Account())
.map(account -> client.getAccount(ctx.getCoinData().getWallet()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
balanceObservable.subscribe(new DefaultObserver<Account>() {
@Override
public void onNext(Account account) {
Log.i(TAG, "getBalance onNext");
// account = client.getAccount(ctx.getCoinData().getWallet());
BinanceData binanceData = (BinanceData) ctx.getCoinData();
for (Balance balance : account.getBalances()) {
if (balance.getSymbol().equals("BNB")) {
binanceData.setBalanceReceived(true);
binanceData.setBalance(balance.getFree());
break;
}
}
if (binanceData instanceof BinanceAssetData) {
BinanceAssetData binanceAssetData = (BinanceAssetData) binanceData;
for (Balance balance : account.getBalances()) {
if (balance.getSymbol().equals(ctx.getCard().getContractAddress())) {
binanceAssetData.setAssetBalance(balance.getFree());
break;
}
}
}
if (!binanceData.isBalanceReceived()) {
binanceData.setBalanceReceived(true);
binanceData.setBalance("0");
}
binanceData.setAccountNumber(account.getAccountNumber());
binanceData.setSequence(account.getSequence());
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "getBalance onError" + e.getMessage());
if (e.getMessage().contains("account not found")) {
((BinanceData)ctx.getCoinData()).setError404(true);
responseListener.onFail();
} else {
e.printStackTrace();
ctx.setError(e.getMessage());
responseListener.onFail();
}
}
@Override
public void onComplete() {
Log.i(TAG, "getBalance onComplete");
responseListener.onSuccess();
}
});
}
public void sendTransaction (byte[] txForSend, BinanceDexApiRestClient client) {
Log.i(TAG, "new sendTransaction request");
RequestBody requestBody = TransactionRequestAssemblerExtSign.createRequestBody(txForSend);
Observable<List<TransactionMetadata>> sendObservable = Observable.just(new ArrayList<>())
.map(metadatas -> client.broadcastNoWallet(requestBody, true))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
sendObservable.subscribe(new DefaultObserver<List<TransactionMetadata>>() {
@Override
public void onNext(List<TransactionMetadata> metadatas ) {
// RequestBody requestBody = TransactionRequestAssemblerExtSign.createRequestBody(txForSend);
// List<TransactionMetadata> metadatas = client.broadcastNoWallet(requestBody, true);
if (!metadatas.isEmpty() && metadatas.get(0).isOk()) {
responseListener.onSuccess();
} else {
Log.e(TAG, "Transaction send error");
responseListener.onFail();
}
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "sendTransaction onError" + e.getMessage());
e.printStackTrace();
responseListener.onFail();
}
@Override
public void onComplete() {
Log.i(TAG, "sendTransaction onComplete");
}
});
}
}

View file

@ -1,47 +0,0 @@
package com.tangem.data.network;
import com.tangem.App;
import com.tangem.data.network.model.BitcoreBalance;
import com.tangem.data.network.model.BitcoreBalanceAndUnspents;
import com.tangem.data.network.model.BitcoreSendBody;
import com.tangem.data.network.model.BitcoreSendResponse;
import com.tangem.data.network.model.BitcoreUtxo;
import com.tangem.tangem_card.util.Log;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Single;
import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class ServerApiBitcore {
private static String TAG = ServerApiBitcore.class.getSimpleName();
public void getBalanceAndUnspents(String wallet, SingleObserver<BitcoreBalanceAndUnspents> balanceAndUnspentsObserver) {
Log.i(TAG, "new getAddressAndUnspents request");
DucatusApi api = App.Companion.getNetworkComponent().getRetrofitDucatus().create(DucatusApi.class);
Single<BitcoreBalance> balanceObservable = api.ducatusBalance(wallet);
Single<List<BitcoreUtxo>> unspentsObservable = api.ducatusUnspents(wallet)
.onErrorReturnItem(new ArrayList<>());
Single.zip(balanceObservable, unspentsObservable, BitcoreBalanceAndUnspents::new)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(balanceAndUnspentsObserver);
}
public void sendTransaction(String tx, SingleObserver<BitcoreSendResponse> sendObserver) {
Log.i(TAG, "new getAddress request");
DucatusApi api = App.Companion.getNetworkComponent().getRetrofitDucatus().create(DucatusApi.class);
Single<BitcoreSendResponse> sendObservable = api.ducatusSend(new BitcoreSendBody(tx))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
sendObservable.subscribe(sendObserver);
}
}

View file

@ -1,62 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import com.tangem.App;
import com.tangem.data.network.model.BlockchainInfoAddress;
import com.tangem.data.network.model.BlockchainInfoAddressAndUnspents;
import com.tangem.data.network.model.BlockchainInfoTransaction;
import com.tangem.data.network.model.BlockchainInfoUnspents;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Single;
import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;
public class ServerApiBlockchainInfo {
private static String TAG = ServerApiBlockchainInfo.class.getSimpleName();
private int page = 1;
public void getAddressAndUnspents(String wallet, SingleObserver<BlockchainInfoAddressAndUnspents> addressAndUnspentsObserver) {
Log.i(TAG, "new getAddressAndUnspents request");
BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class);
Single<BlockchainInfoAddress> addressObservable = api.blockchainInfoAddress(wallet, null);
Single<BlockchainInfoUnspents> unspentsObservable = api.blockchainInfoUnspents(wallet)
.onErrorReturnItem(new BlockchainInfoUnspents(new ArrayList<>()));
Single.zip(addressObservable, unspentsObservable, BlockchainInfoAddressAndUnspents::new)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(addressAndUnspentsObserver);
}
public void sendTransaction(String tx, SingleObserver<ResponseBody> sendObserver) {
Log.i(TAG, "new getAddress request");
BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class);
Single<ResponseBody> sendObservable = api.blockchainInfoPush(tx)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
sendObservable.subscribe(sendObserver);
}
public Single<List<BlockchainInfoTransaction>> getMoreAddressTxs(String wallet) {
Log.i(TAG, "new getAddress request");
BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class);
Single<List<BlockchainInfoTransaction>> addressObservable = api.blockchainInfoAddress(wallet, page * 50)
.map(BlockchainInfoAddress::getTxs)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
page++;
return addressObservable;
}
}

View file

@ -1,73 +0,0 @@
package com.tangem.data.network;
import com.tangem.App;
import com.tangem.data.Blockchain;
import com.tangem.data.network.model.BlockchairAddressResponse;
import com.tangem.data.network.model.BlockchairSendBody;
import com.tangem.data.network.model.BlockchairStatsResponse;
import com.tangem.data.network.model.BlockchairTransactionResponse;
import com.tangem.tangem_card.util.Log;
import io.reactivex.Completable;
import io.reactivex.CompletableObserver;
import io.reactivex.Single;
import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class ServerApiBlockchair {
private static String TAG = ServerApiBlockchair.class.getSimpleName();
private String blockchain;
public ServerApiBlockchair(Blockchain blockchain) {
if (blockchain == Blockchain.BitcoinCash) this.blockchain = "bitcoin-cash";
}
public void getAddress(String wallet, SingleObserver<BlockchairAddressResponse> addressObserver) {
Log.i(TAG, "new getAddress request");
BlockchairApi api = App.Companion.getNetworkComponent().getRetrofitBlockchair().create(BlockchairApi.class);
Single<BlockchairAddressResponse> addressSingle = api.getAddress(blockchain, wallet)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
addressSingle.subscribe(addressObserver);
}
public void getTransaction(String transaction, SingleObserver<BlockchairTransactionResponse> transactionObserver) {
Log.i(TAG, "new getAddress request");
BlockchairApi api = App.Companion.getNetworkComponent().getRetrofitBlockchair().create(BlockchairApi.class);
Single<BlockchairTransactionResponse> transactionSingle = api.getTransaction(blockchain, transaction)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
transactionSingle.subscribe(transactionObserver);
}
public void getStats(SingleObserver<BlockchairStatsResponse> statsObserver) {
Log.i(TAG, "new getStats request");
BlockchairApi api = App.Companion.getNetworkComponent().getRetrofitBlockchair().create(BlockchairApi.class);
Single<BlockchairStatsResponse> statsSingle = api.getStats(blockchain)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
statsSingle.subscribe(statsObserver);
}
public void sendTransaction(String tx, CompletableObserver sendObserver) {
Log.i(TAG, "new getAddress request");
BlockchairApi api = App.Companion.getNetworkComponent().getRetrofitDucatus().create(BlockchairApi.class);
Completable sendCompletable = api.sendTransaction(blockchain, new BlockchairSendBody(tx))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
sendCompletable.subscribe(sendObserver);
}
public String getUrl() {
return ServerURL.API_BLOCKCHAIR;
}
}

View file

@ -1,212 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.App;
import com.tangem.data.Blockchain;
import com.tangem.data.network.model.BlockcypherBody;
import com.tangem.data.network.model.BlockcypherFee;
import com.tangem.data.network.model.BlockcypherResponse;
import com.tangem.data.network.model.BlockcypherTx;
import java.util.Random;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiBlockcypher {
private static String TAG = ServerApiBlockcypher.class.getSimpleName();
public static final String BLOCKCYPHER_ADDRESS = "blockcypher_address";
public static final String BLOCKCYPHER_FEE = "blockcypher_fee";
public static final String BLOCKCYPHER_TXS = "blockcypher_txs";
public static final String BLOCKCYPHER_SEND = "blockcypher_send";
private int requestsCount = 0;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private String apiKey = null;
private ResponseListener responseListener;
private TxResponseListener txResponseListener;
public interface ResponseListener {
void onSuccess(String method, BlockcypherResponse blockcypherResponse);
void onSuccess(String method, BlockcypherFee blockcypherFee);
void onFail(String method, String message);
}
public interface TxResponseListener {
void onSuccess(BlockcypherTx blockcypherTx);
void onFail(String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void setTxResponseListener(TxResponseListener txListener) {
txResponseListener = txListener;
}
public void requestData(String blockchainID, String method, String wallet, String tx) {
requestsCount++;
String blockchain = blockchainID.toLowerCase();
BlockcypherApi blockcypherApi = App.Companion.getNetworkComponent().getRetrofitBlockcypher().create(BlockcypherApi.class);
String network = "main";
if (blockchainID.equals(Blockchain.BitcoinTestNet.getID())) {
blockchain = "btc";
network = "test3";
}
if (blockchainID.equals(Blockchain.Token.getID())) blockchain = "eth";
if (blockchainID.equals(Blockchain.BitcoinDual.getID())) blockchain = "btc";
switch (method) {
case BLOCKCYPHER_ADDRESS:
Call<BlockcypherResponse> addressCall = blockcypherApi.blockcypherAddress(blockchain, network, wallet, apiKey);
addressCall.enqueue(new Callback<BlockcypherResponse>() {
@Override
public void onResponse(@NonNull Call<BlockcypherResponse> call, @NonNull Response<BlockcypherResponse> response) {
requestsCount--;
switch (response.code()) {
case 200:
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
break;
case 429:
apiKey = getRandomApiKey();
requestData(blockchainID, method, wallet, tx);
break;
default:
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
break;
}
}
@Override
public void onFailure(@NonNull Call<BlockcypherResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
break;
case BLOCKCYPHER_FEE:
Call<BlockcypherFee> feeCall = blockcypherApi.blockcypherMain(blockchain, network, apiKey);
feeCall.enqueue(new Callback<BlockcypherFee>() {
@Override
public void onResponse(@NonNull Call<BlockcypherFee> call, @NonNull Response<BlockcypherFee> response) {
requestsCount--;
switch (response.code()) {
case 200:
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
break;
case 429:
apiKey = getRandomApiKey();
requestData(blockchainID, method, wallet, tx);
break;
default:
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
break;
}
}
@Override
public void onFailure(@NonNull Call<BlockcypherFee> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
break;
case BLOCKCYPHER_TXS:
Call<BlockcypherTx> txsCall = blockcypherApi.blockcypherTxs(blockchain, network, tx, apiKey);
txsCall.enqueue(new Callback<BlockcypherTx>() {
@Override
public void onResponse(@NonNull Call<BlockcypherTx> call,@NonNull Response<BlockcypherTx> response) {
requestsCount--;
switch (response.code()) {
case 200:
txResponseListener.onSuccess(response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
break;
case 429:
apiKey = getRandomApiKey();
requestData(blockchainID, method, wallet, tx);
break;
default:
txResponseListener.onFail(String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
break;
}
}
@Override
public void onFailure(@NonNull Call<BlockcypherTx> call, @NonNull Throwable t) {
requestsCount--;
txResponseListener.onFail(String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
break;
case BLOCKCYPHER_SEND:
Call<BlockcypherResponse> sendCall = blockcypherApi.blockcypherPush(blockchain, network, new BlockcypherBody(tx), apiKey);
sendCall.enqueue(new Callback<BlockcypherResponse>() {
@Override
public void onResponse(@NonNull Call<BlockcypherResponse> call, @NonNull Response<BlockcypherResponse> response) {
requestsCount--;
switch (response.code()) {
case 201:
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
break;
case 429:
apiKey = getRandomApiKey();
requestData(blockchainID, method, wallet, tx);
break;
default:
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
break;
}
}
@Override
public void onFailure(@NonNull Call<BlockcypherResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
break;
default:
requestsCount--;
responseListener.onFail(method, "undeclared method");
Log.e(TAG, "requestData " + method + " onFailure - undeclared method");
break;
}
}
private String getRandomApiKey() {
return BlockcypherToken
.values()[new Random().nextInt(BlockcypherToken.values().length)].getToken();
}
}

View file

@ -1,172 +0,0 @@
package com.tangem.data.network;
import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import android.util.Log;
import com.tangem.App;
import com.tangem.data.network.model.RateInfoResponse;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiCommon {
private static String TAG = ServerApiCommon.class.getSimpleName();
/**
* HTTP
* Estimate fee
*/
public static final int ESTIMATE_FEE_PRIORITY = 2;
public static final int ESTIMATE_FEE_NORMAL = 3;
public static final int ESTIMATE_FEE_MINIMAL = 6;
private EstimatedFeeListener estimatedFeeListener;
public interface EstimatedFeeListener {
void onSuccess(int blockCount, String estimateFeeResponse);
void onFail(int blockCount, String message);
}
public void setBtcEstimatedFeeListener(EstimatedFeeListener listener) {
estimatedFeeListener = listener;
}
public void requestBtcEstimatedFee(int blockCount) {
EstimatefeeApi estimatefeeApi = App.Companion.getNetworkComponent().getRetrofitEstimatefee().create(EstimatefeeApi.class);
Call<String> call;
switch (blockCount) {
case ESTIMATE_FEE_PRIORITY:
call = estimatefeeApi.getEstimateFeePriority();
break;
case ESTIMATE_FEE_NORMAL:
call = estimatefeeApi.getEstimateFeeNormal();
break;
case ESTIMATE_FEE_MINIMAL:
call = estimatefeeApi.getEstimateFeeMinimal();
break;
default:
call = estimatefeeApi.getEstimateFeeNormal();
}
call.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
if (response.code() == 200) {
estimatedFeeListener.onSuccess(blockCount, response.body());
Log.i(TAG, "requestBtcEstimatedFee onResponse " + response.code() + " " + response.body());
} else
estimatedFeeListener.onFail(blockCount, response.body());
Log.e(TAG, "requestBtcEstimatedFee onResponse " + response.code());
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
estimatedFeeListener.onFail(blockCount, t.getMessage());
Log.e(TAG, "requestBtcEstimatedFee onFailure " + t.getMessage());
}
});
}
/**
* HTTP
* Used in Crypto-currency course
*/
private RateInfoListener rateInfoListener;
public interface RateInfoListener {
void onSuccess(RateInfoResponse rateInfoResponse);
void onFail(String message);
}
public void setRateInfoListener(RateInfoListener listener) {
rateInfoListener = listener;
}
public void requestRateInfo(String cryptoId) {
CoinmarketApi coinmarketApi = App.Companion.getNetworkComponent().getRetrofitCoinmarketcap().create(CoinmarketApi.class);
// Call<RateInfoResponse> call = coinmarketApi.getRateInfo(1, cryptoId);
//
// call.enqueue(new Callback<RateInfoResponse>() {
// @Override
// public void onResponse(@NonNull Call<RateInfoResponse> call, @NonNull Response<RateInfoResponse> response) {
// if (response.code() == 200) {
// rateInfoListener.onSuccess(response.body());
// Log.i(TAG, "coinmarketcap onResponse " + response.code());
// } else {
// rateInfoListener.onFail("Rate info error:" + String.valueOf(response.code()));
// Log.e(TAG, "coinmarketcap onResponse " + response.code());
// }
// }
//
// @Override
// public void onFailure(@NonNull Call<RateInfoResponse> call, @NonNull Throwable t) {
// rateInfoListener.onFail(String.valueOf(t.getMessage()));
// Log.e(TAG, "coinmarketcap onFailure " + t.getMessage());
// }
// });
coinmarketApi.getRateInfo(1, cryptoId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(rateInfoResponse -> {
if (rateInfoResponse.getData().getQuote().getUsd().getPrice() != null) {
rateInfoListener.onSuccess(rateInfoResponse);
Log.i(TAG, "coinmarketcap onResponse " + 200);
} else {
rateInfoListener.onFail("Rate info error: wrong response");
}
},
// handle error
Throwable::printStackTrace);
}
/**
* HTTP
* Last version request from GitHub
*/
private LastVersionListener lastVersionListener;
public interface LastVersionListener {
void onSuccess(String lastVersion);
}
public void setLastVersionListener(LastVersionListener listener) {
lastVersionListener = listener;
}
public void requestLastVersion() {
UpdateVersionApi updateVersionApi = App.Companion.getNetworkComponent().getRetrofitGitHubUserContent().create(UpdateVersionApi.class);
Call<ResponseBody> call = updateVersionApi.getLastVersion();
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
Log.i(TAG, "requestLastVersion onResponse " + response.code());
if (response.code() == 200) {
String stringResponse;
try {
stringResponse = response.body() != null ? response.body().string() : null;
lastVersionListener.onSuccess(stringResponse);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
Log.e(TAG, "lastVersion onFailure " + t.getMessage());
}
});
}
}

View file

@ -1,386 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import com.tangem.App;
import com.tangem.data.Blockchain;
import com.tangem.wallet.R;
import com.tangem.wallet.TangemContext;
import com.tangem.wallet.bch.BitcoinCashNode;
import com.tangem.wallet.btc.BitcoinNode;
import com.tangem.wallet.btc.BitcoinNodeTestNet;
import com.tangem.wallet.ltc.LitecoinNode;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DefaultObserver;
import io.reactivex.schedulers.Schedulers;
/**
* Request processor for Electrum Api
* Every request live cycle:
* 1. In application create request and call {@link ServerApiElectrum}.requestData(..)
* 2. Try send every request for max 4 times,
* 3. If all 4 times fail call DefaultObserver<ElectrumRequest>.onError (defined in .requestData(..)) and than
* {@link ResponseListener}.onFail(...) callback
* Error can be acquired with {@link ElectrumRequest}.getError() method
* 4. If request network communication finished successfully then call DefaultObserver<ElectrumRequest>.onComplete (defined in .requestData) and than
* {@link ResponseListener}.onSuccess(...) callback
*/
public class ServerApiElectrum {
public static final String ERROR_STARTS_WITH_CODE_32601 = "{\"code\":-32601,";
private static String TAG = ServerApiElectrum.class.getSimpleName();
/**
* TCP, SSL
* Used in BTC, BCH
*/
private ResponseListener responseListener;
private String host;
private int port;
private int requestsCount=0;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
/**
* Interface for notification every request result
*/
public interface ResponseListener {
/**
* Notify that request processing was successful
* @param electrumRequest - processed request containing received answer {@see electrumRequest.getAnswer() method}
*/
void onSuccess(ElectrumRequest electrumRequest);
/**
* Notify that request processing was successful
* @param electrumRequest - processed request containing occurred error {@see electrumRequest.getError() method}
*/
void onFail(ElectrumRequest electrumRequest);
}
/**
* Set notificaion listener
* @param listener
*/
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
/**
* Start process request
* @param ctx
* @param electrumRequest
*/
public void requestData(TangemContext ctx, ElectrumRequest electrumRequest) {
requestsCount++;
Log.i(TAG, String.format("New request[%d]: %s", requestsCount,electrumRequest.getMethod()));
Observable<ElectrumRequest> checkElectrumDataObserver = Observable.just(electrumRequest)
.doOnNext(electrumRequest1 -> doElectrumRequest(ctx, electrumRequest))
.flatMap(electrumRequest1 -> {
if (electrumRequest1.answerData == null) {
Log.e(TAG, "NullPointerException " + electrumRequest.getMethod());
return Observable.error(new NullPointerException());
} else
return Observable.just(electrumRequest1);
})
.retryWhen(errors -> errors
.filter(throwable -> throwable instanceof NullPointerException)
.zipWith(Observable.range(1, 4), (n, i) -> i))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
checkElectrumDataObserver.subscribe(new DefaultObserver<ElectrumRequest>() {
//TODO remove onNext
@Override
public void onNext(ElectrumRequest v) {
if (electrumRequest.answerData != null) {
Log.i(TAG, "requestData " + electrumRequest.getMethod() + " onNext != null");
} else {
Log.e(TAG, "requestData " + electrumRequest.getMethod() + " onNext == null");
}
}
@Override
public void onError(Throwable e) {
requestsCount--;
Log.e(TAG, "requestData " + electrumRequest.getMethod() + " onError " + e.getMessage());
Log.e(TAG, String.format("%d requests left in processing",requestsCount));
electrumRequest.setError(ctx.getString(R.string.loaded_wallet_error_obtaining_blockchain_data));
//setErrorOccurred(e.getMessage());//;
responseListener.onFail(electrumRequest);
}
/**
* Called after completion request processing
*/
@Override
public void onComplete() {
requestsCount--;
if (electrumRequest.answerData != null) {
Log.i(TAG, "requestData " + electrumRequest.getMethod() + " onComplete, answerData!=null");
} else {
Log.e(TAG, "requestData " + electrumRequest.getMethod() + " onComplete, answerData==null");
}
Log.e(TAG, String.format("%d requests left in processing",requestsCount));
if (electrumRequest.answerData != null && electrumRequest.getError()==null) {
responseListener.onSuccess(electrumRequest);
} else {
// if( error==null || error.isEmpty() ) setErrorOccurred(ctx.getString(R.string.cannot_obtain_data_from_blockchain));
responseListener.onFail(electrumRequest);
}
}
});
}
private void doElectrumRequest(TangemContext ctx, ElectrumRequest electrumRequest) {
String host;
int port;
String proto;
electrumRequest.setError(null);
// todo - get available URL list from coinEngine, remove if( ctx.getBlockchain()==...)
if (ctx.getBlockchain() == Blockchain.BitcoinTestNet) {
BitcoinNodeTestNet bitcoinNodeTestNet = BitcoinNodeTestNet.values()[new Random().nextInt(BitcoinNodeTestNet.values().length)];
host = bitcoinNodeTestNet.getHost();
port = bitcoinNodeTestNet.getPort();
this.host = host;
this.port = port;
doElectrumRequestTcp(electrumRequest, host, port);
} else if (ctx.getBlockchain() == Blockchain.BitcoinCash) {
BitcoinCashNode bitcoinCashNode = BitcoinCashNode.values()[new Random().nextInt(BitcoinCashNode.values().length)];
host = bitcoinCashNode.getHost();
port = bitcoinCashNode.getPort();
proto = bitcoinCashNode.getProto();
this.host = host;
this.port = port;
if (proto.equals("tcp")) {
doElectrumRequestTcp(electrumRequest, host, port);
} else {
doElectrumRequestSsl(electrumRequest, host, port);
}
} else if (ctx.getBlockchain() == Blockchain.Bitcoin) {
BitcoinNode bitcoinNode = BitcoinNode.values()[new Random().nextInt(BitcoinNode.values().length)];
host = bitcoinNode.getHost();
port = bitcoinNode.getPort();
proto = bitcoinNode.getProto();
this.host = host;
this.port = port;
if (proto.equals("tcp")) {
doElectrumRequestTcp(electrumRequest, host, port);
} else {
doElectrumRequestSsl(electrumRequest, host, port);
}
} else if (ctx.getBlockchain() == Blockchain.Litecoin) {
LitecoinNode litecoinNode = LitecoinNode.values()[new Random().nextInt(LitecoinNode.values().length)];
host = litecoinNode.getHost();
port = litecoinNode.getPort();
proto = litecoinNode.getProto();
this.host = host;
this.port = port;
if (proto.equals("tcp")) {
doElectrumRequestTcp(electrumRequest, host, port);
} else {
doElectrumRequestSsl(electrumRequest, host, port);
}
}
}
private void doElectrumRequestTcp(ElectrumRequest electrumRequest, String host, int port) {
try {
Socket socket = App.Companion.getNetworkComponent().getSocket();
socket.setSoTimeout(3000);
Log.i(TAG, "Start process "+electrumRequest.getMethod()+" @ "+host + ":" + port);
socket.connect(new InetSocketAddress(InetAddress.getByName(host), port));
try {
OutputStream os = socket.getOutputStream();
OutputStreamWriter out = new OutputStreamWriter(os, "UTF-8");
InputStream is = socket.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(is));
electrumRequest.setID(1);
out.write(electrumRequest.getAsString() + "\n");
out.flush();
electrumRequest.answerData = in.readLine();
electrumRequest.host = host;
electrumRequest.port = port;
if (electrumRequest.answerData != null) {
Log.i(TAG, ">> " + electrumRequest.answerData);
if( (electrumRequest.getError()!=null && electrumRequest.getError().startsWith(ERROR_STARTS_WITH_CODE_32601)) )
{
// method unknown error???
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_obtaining_blockchain_data));
electrumRequest.answerData=null;
}
} else {
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_blockchain_empty_answer));
Log.i(TAG, ">> <NULL>");
}
} catch (ConnectException e) {
//e.printStackTrace();
//responseListener.onFail(e.getMessage());
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_blockchain_connection_refused));
Log.e(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " ConnectException " + e.getMessage());
} finally {
Log.i(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " socket.close");
try {
if( socket.isConnected() ) socket.close();
}
catch (Exception e)
{
e.printStackTrace();
Log.e(TAG,"Can't close socket");
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_blockchain_communication_error));
}
}
} catch (IOException e) {
//e.printStackTrace();
//responseListener.onFail(e.getMessage());
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_blockchain_communication_error));
Log.e(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " IOException " + e.getMessage());
}
}
private void doElectrumRequestSsl(ElectrumRequest electrumRequest, String host, int port) {
try {
// create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}};
// install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
SSLSocketFactory sf = sc.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(sf);
// create all-trusting host name verifier
HostnameVerifier allHostsValid = (hostname, session) -> true;
// install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
Socket sslSocket = new Socket();
List<ElectrumRequest> result = new ArrayList<>();
Collections.addAll(result, electrumRequest);
try {
Log.i(TAG, host + " " + port);
sslSocket.connect(new InetSocketAddress(host,port), 3000);
sslSocket.setSoTimeout(3000);
sslSocket = sf.createSocket(sslSocket, host, port, true);
try {
OutputStream os = sslSocket.getOutputStream();
OutputStreamWriter out = new OutputStreamWriter(os, "UTF-8");
InputStream is = sslSocket.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(is));
electrumRequest.setID(1);
out.write(electrumRequest.getAsString() + "\n");
out.flush();
electrumRequest.answerData = in.readLine();
if (electrumRequest.answerData != null) {
Log.i(TAG, ">> " + electrumRequest.answerData);
if( (electrumRequest.getError()!=null && electrumRequest.getError().startsWith(ERROR_STARTS_WITH_CODE_32601)) )
{
// method unknown error???
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_obtaining_blockchain_data));
electrumRequest.answerData=null;
}
} else {
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_blockchain_empty_answer));
Log.i(TAG, ">> <NULL>");
}
} catch (ConnectException e) {
e.printStackTrace();
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_blockchain_connection_refused));
Log.e(TAG, "doElectrumRequestSsl " + electrumRequest.getMethod() + " ConnectException " + e.getMessage());
} finally {
Log.i(TAG, "doElectrumRequestSsl " + electrumRequest.getMethod() + " socket.close");
try {
if( sslSocket.isConnected() ) sslSocket.close();
}
catch (Exception e)
{
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_blockchain_communication_error));
e.printStackTrace();
Log.e(TAG, "Can't close ssl socket");
}
}
} catch (IOException e) {
e.printStackTrace();
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_blockchain_communication_error));
Log.e(TAG, "doElectrumRequestSsl " + electrumRequest.getMethod() + " IOException " + e.getMessage());
}
} catch (NoSuchAlgorithmException | KeyManagementException e) {
electrumRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_obtaining_blockchain_data));
Log.e(TAG, e.getMessage());
}
}
public String getValidationNodeDescription() {
return "Electrum, " + host + ":" + String.valueOf(port);
}
}

View file

@ -1,46 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import com.tangem.wallet.eos.EosApiPush;
import com.tangem.wallet.eos.EosPushTransactionRequest;
import io.jafka.jeos.EosApi;
import io.jafka.jeos.EosApiFactory;
import io.jafka.jeos.core.response.chain.account.Account;
import io.jafka.jeos.core.response.chain.transaction.PushedTransaction;
import io.jafka.jeos.impl.EosApiServiceGenerator;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class ServerApiEos {
private static String TAG = ServerApiEos.class.getSimpleName();
public static void getBalance(String wallet, Observer<Account> accountObserver) {
Log.i(TAG, "new getBalance request");
EosApi eosApi = EosApiFactory.create("https://api.eosdetroit.io:443"); //TODO: add random server request
Observable<Account> accountObservable = Observable.just(new Account())
.map(account -> eosApi.getAccount(wallet))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
accountObservable.subscribe(accountObserver);
}
public static void sendTransaction(EosPushTransactionRequest req, Observer<PushedTransaction> sendObserver) {
Log.i(TAG, "new getBalance request");
// EosApi eosApi = EosApiFactory.create("https://api.eosdetroit.io:443"); //TODO: add random server request
EosApiPush eosApiPush = EosApiServiceGenerator.createService(EosApiPush.class, "https://api.eosdetroit.io:443"); //TODO: add random server request
Observable<PushedTransaction> sendObservable = Observable.just(new PushedTransaction())
.map(pushedTransaction -> EosApiServiceGenerator.executeSync(eosApiPush.pushTransaction(req)))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
sendObservable.subscribe(sendObserver);
}
}

View file

@ -1,121 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.App;
import com.tangem.data.Blockchain;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiInfura {
private static String TAG = ServerApiInfura.class.getSimpleName();
/**
* HTTP
* Infura
* <p>
* eth_getBalance
* eth_getTransactionCount
* eth_call
* eth_sendRawTransaction
* eth_gasPrice
*/
public static final String INFURA_ETH_GET_BALANCE = "eth_getBalance";
public static final String INFURA_ETH_GET_TRANSACTION_COUNT = "eth_getTransactionCount";
public static final String INFURA_ETH_GET_PENDING_COUNT = "eth_getPendingCount";
public static final String INFURA_ETH_CALL = "eth_call";
public static final String INFURA_ETH_SEND_RAW_TRANSACTION = "eth_sendRawTransaction";
public static final String INFURA_ETH_GAS_PRICE = "eth_gasPrice";
private int requestsCount=0;
private InfuraApi infuraApi = App.Companion.getNetworkComponent().getRetrofitInfura().create(InfuraApi.class);
public 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);
}
}
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, InfuraResponse infuraResponse);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, int id, String wallet, String contract, String tx) {
requestsCount++;
InfuraBody infuraBody;
switch (method) {
case INFURA_ETH_GET_BALANCE:
case INFURA_ETH_GET_TRANSACTION_COUNT:
infuraBody = new InfuraBody(method, new String[]{wallet, "latest"}, id);
break;
case INFURA_ETH_GET_PENDING_COUNT:
infuraBody = new InfuraBody(INFURA_ETH_GET_TRANSACTION_COUNT, new String[]{wallet, "pending"}, id);
break;
case INFURA_ETH_CALL:
String address = wallet.substring(2);
infuraBody = new InfuraBody(method, new Object[]{new InfuraBody.EthCallParams("0x70a08231000000000000000000000000" + address, contract), "latest"}, id);
break;
case INFURA_ETH_SEND_RAW_TRANSACTION:
infuraBody = new InfuraBody(method, new String[]{tx}, id);
break;
case INFURA_ETH_GAS_PRICE:
infuraBody = new InfuraBody(method, id);
break;
default:
infuraBody = new InfuraBody();
}
Call<InfuraResponse> call = infuraApi.infura(infuraBody);
call.enqueue(new Callback<InfuraResponse>() {
@Override
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
requestsCount--;
if (response.code() == 200) {
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}
}

View file

@ -1,131 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.data.network.model.InsightBody;
import com.tangem.data.network.model.InsightResponse;
import com.tangem.data.network.model.InsightUtxo;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ServerApiInsight {
private static String TAG = ServerApiInsight.class.getSimpleName();
public static final String INSIGHT_ADDRESS = "addr/{address}";
public static final String INSIGHT_UNSPENT_OUTPUTS = "addr/{address}/utxo";
public static final String INSIGHT_TRANSACTION = "rawtx/{txId}";
public static final String INSIGHT_FEE = "utils/estimatefee?nbBlocks=2,3,6";
public static final String INSIGHT_SEND = "tx/send";
private int requestsCount = 0;
public static String lastNode;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, InsightResponse insightResponse);
void onSuccess(String method, List<InsightUtxo> utxoList);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, String wallet, String tx) {
requestsCount++;
String insightURL = "https://insight.ducatus.io/insight-lite-api/"; //TODO: make random selection
this.lastNode = insightURL; //TODO: show node instead of URL
Retrofit retrofitInsight = new Retrofit.Builder()
.baseUrl(insightURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
// InsightApi insightApi = App.getNetworkComponent().getRetrofitInsight(insightURL).create(InsightApi.class);
InsightApi insightApi = retrofitInsight.create(InsightApi.class);
if (method.equals(INSIGHT_UNSPENT_OUTPUTS)) {
Call<List<InsightUtxo>> call = insightApi.insightUnspent(wallet);
call.enqueue(new Callback<List<InsightUtxo>>() {
@Override
public void onResponse(@NonNull Call<List<InsightUtxo>> call, @NonNull Response<List<InsightUtxo>> response) {
requestsCount--;
if (response.code() == 200) {
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<List<InsightUtxo>> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
} else {
Call<InsightResponse> call;
switch (method) {
case INSIGHT_ADDRESS:
call = insightApi.insightAddress(wallet);
break;
// case INSIGHT_FEE:
// call = insightApi.insightFee();
// break;
case INSIGHT_SEND:
call = insightApi.insightSend(new InsightBody(tx));
break;
default:
call = insightApi.insightAddress(wallet);
break;
}
call.enqueue(new Callback<InsightResponse>() {
@Override
public void onResponse(@NonNull Call<InsightResponse> call, @NonNull Response<InsightResponse> response) {
requestsCount--;
if (response.code() == 200) {
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InsightResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}
}
}

View file

@ -1,108 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.App;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiMatic {
private static String TAG = ServerApiMatic.class.getSimpleName();
/**
* HTTP
* Infura
* <p>
* eth_getBalance
* eth_getTransactionCount
* eth_call
* eth_sendRawTransaction
* eth_gasPrice
*/
public static final String MATIC_ETH_GET_BALANCE = "eth_getBalance";
public static final String MATIC_ETH_GET_TRANSACTION_COUNT = "eth_getTransactionCount";
public static final String MATIC_ETH_GET_PENDING_COUNT = "eth_getPendingCount";
public static final String MATIC_ETH_CALL = "eth_call";
public static final String MATIC_ETH_SEND_RAW_TRANSACTION = "eth_sendRawTransaction";
public static final String MATIC_ETH_GAS_PRICE = "eth_gasPrice";
private int requestsCount=0;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, InfuraResponse infuraResponse);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, int id, String wallet, String contract, String tx) {
requestsCount++;
MaticApi maticApi = App.Companion.getNetworkComponent().getRetrofitMaticTesnet().create(MaticApi.class);
InfuraBody infuraBody;
switch (method) {
case MATIC_ETH_GET_BALANCE:
case MATIC_ETH_GET_TRANSACTION_COUNT:
infuraBody = new InfuraBody(method, new String[]{wallet, "latest"}, id);
break;
case MATIC_ETH_GET_PENDING_COUNT:
infuraBody = new InfuraBody(MATIC_ETH_GET_TRANSACTION_COUNT, new String[]{wallet, "pending"}, id);
break;
case MATIC_ETH_CALL:
String address = wallet.substring(2);
infuraBody = new InfuraBody(method, new Object[]{new InfuraBody.EthCallParams("0x70a08231000000000000000000000000" + address, contract), "latest"}, id);
break;
case MATIC_ETH_SEND_RAW_TRANSACTION:
infuraBody = new InfuraBody(method, new String[]{tx}, id);
break;
case MATIC_ETH_GAS_PRICE:
infuraBody = new InfuraBody(method, id);
break;
default:
infuraBody = new InfuraBody();
}
Call<InfuraResponse> call = maticApi.matic(infuraBody);
call.enqueue(new Callback<InfuraResponse>() {
@Override
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
requestsCount--;
if (response.code() == 200) {
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}
}

View file

@ -1,74 +0,0 @@
package com.tangem.data.network;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.data.Blockchain;
import com.tangem.data.network.model.PayIdResponse;
import com.tangem.tangem_card.util.Log;
import java.security.InvalidParameterException;
import io.reactivex.Single;
import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ServerApiPayId {
private static String TAG = ServerApiPayId.class.getSimpleName();
private int requestsCount = 0;
public String getAcceptHeader(Blockchain blockchain) throws InvalidParameterException {
switch (blockchain) {
case Ripple: return "application/xrpl-mainnet+json";
case Bitcoin: return "application/btc-mainnet+json";
case Litecoin: return "application/ltc-mainnet+json";
case Cardano: return "application/ada-mainnet+json";
case Ducatus: return "application/duc-mainnet+json";
case BitcoinCash: return "application/bch-mainnet+json";
case Ethereum:
case Token:
return "application/eth-mainnet+json";
case Stellar:
case StellarAsset:
return "application/xlm-mainnet+json";
case Binance:
case BinanceAsset:
return "application/bnb-mainnet+json";
case Rootstock:
case RootstockToken:
return "application/rsk-mainnet+json";
default: throw new InvalidParameterException("PayID is not supported for " + blockchain.getOfficialName());
}
}
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
public void getAddress(String payID, Blockchain blockchain, SingleObserver<PayIdResponse> addressObserver) throws InvalidParameterException {
requestsCount++;
Log.i(TAG, "new getAddress request");
String[] addressParts = payID.split("\\$");
String user = addressParts[0];
String domain = addressParts[1];
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + domain + "/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
PayIdApi api = retrofit.create(PayIdApi.class);
Single<PayIdResponse> addressSingle = api.getAddress(user, getAcceptHeader(blockchain))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((object, throwable) -> requestsCount--);
addressSingle.subscribe(addressObserver);
}
}

View file

@ -1,157 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.data.network.model.RippleBody;
import com.tangem.data.network.model.RippleResponse;
import java.util.HashMap;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ServerApiRipple {
private static String TAG = ServerApiRipple.class.getSimpleName();
public static final String RIPPLE_ACCOUNT_INFO = "account_info";
public static final String RIPPLE_ACCOUNT_UNCONFIRMED = "account_unconfirmed";
public static final String RIPPLE_SUBMIT = "submit";
public static final String RIPPLE_FEE = "fee";
public static final String RIPPLE_SERVER_STATE = "server_state";
private int requestsCount = 0;
private final String rippleURL1 = "https://s1.ripple.com:51234"; //TODO: make random selection, add more?, move
private final String rippleURL2 = "https://s2.ripple.com:51234";
private String currentURL = rippleURL1;
public String getCurrentURL() {
return currentURL;
}
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, RippleResponse rippleResponse);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, String wallet, String tx) {
requestsCount++;
Retrofit retrofitRipple = new Retrofit.Builder()
.baseUrl(currentURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
RippleApi rippleApi = retrofitRipple.create(RippleApi.class);
RippleBody rippleBody;
HashMap<String, String> paramsMap;
switch (method) {
case RIPPLE_ACCOUNT_INFO:
paramsMap = new HashMap<>();
paramsMap.put("account", wallet);
paramsMap.put("ledger_index", "validated");
rippleBody = new RippleBody(method, paramsMap);
break;
case RIPPLE_ACCOUNT_UNCONFIRMED:
paramsMap = new HashMap<>();
paramsMap.put("account", wallet);
paramsMap.put("ledger_index", "current");
// paramsMap.put("queue", "true"); TODO: make queue check if needed
rippleBody = new RippleBody(RIPPLE_ACCOUNT_INFO, paramsMap);
break;
case RIPPLE_SERVER_STATE:
rippleBody = new RippleBody(method, new HashMap<>());
break;
case RIPPLE_FEE:
rippleBody = new RippleBody(method, new HashMap<>());
break;
case RIPPLE_SUBMIT:
paramsMap = new HashMap<>();
paramsMap.put("tx_blob", tx);
rippleBody = new RippleBody(method, paramsMap);
break;
default:
rippleBody = new RippleBody();
}
Call<RippleResponse> call = rippleApi.ripple(rippleBody);
call.enqueue(new Callback<RippleResponse>() {
@Override
public void onResponse(@NonNull Call<RippleResponse> call, @NonNull Response<RippleResponse> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
retryRequest(method, rippleBody);
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<RippleResponse> call, @NonNull Throwable t) {
retryRequest(method, rippleBody);
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}
private void retryRequest(String method, RippleBody rippleBody) {
currentURL = rippleURL2;
Retrofit retrofitRipple = new Retrofit.Builder()
.baseUrl(currentURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
RippleApi rippleApi = retrofitRipple.create(RippleApi.class);
Call<RippleResponse> call = rippleApi.ripple(rippleBody);
call.enqueue(new Callback<RippleResponse>() {
@Override
public void onResponse(@NonNull Call<RippleResponse> call, @NonNull Response<RippleResponse> response) {
requestsCount--;
if (response.code() == 200) {
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<RippleResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}
}

View file

@ -1,108 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.App;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiRootstock {
private static String TAG = ServerApiRootstock.class.getSimpleName();
/**
* HTTP
* Rootstock
* <p>
* eth_getBalance
* eth_getTransactionCount
* eth_call
* eth_sendRawTransaction
* eth_gasPrice
*/
public static final String ROOTSTOCK_ETH_GET_BALANCE = "eth_getBalance";
public static final String ROOTSTOCK_ETH_GET_TRANSACTION_COUNT = "eth_getTransactionCount";
public static final String ROOTSTOCK_ETH_GET_PENDING_COUNT = "eth_getPendingCount";
public static final String ROOTSTOCK_ETH_CALL = "eth_call";
public static final String ROOTSTOCK_ETH_SEND_RAW_TRANSACTION = "eth_sendRawTransaction";
public static final String ROOTSTOCK_ETH_GAS_PRICE = "eth_gasPrice";
private int requestsCount=0;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, InfuraResponse infuraResponse);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, int id, String wallet, String contract, String tx) {
requestsCount++;
RootstockApi rootstockApi = App.Companion.getNetworkComponent().getRetrofitRootstock().create(RootstockApi.class);
InfuraBody infuraBody;
switch (method) {
case ROOTSTOCK_ETH_GET_BALANCE:
case ROOTSTOCK_ETH_GET_TRANSACTION_COUNT:
infuraBody = new InfuraBody(method, new String[]{wallet, "latest"}, id);
break;
case ROOTSTOCK_ETH_GET_PENDING_COUNT:
infuraBody = new InfuraBody(ROOTSTOCK_ETH_GET_TRANSACTION_COUNT, new String[]{wallet, "pending"}, id);
break;
case ROOTSTOCK_ETH_CALL:
String address = wallet.substring(2);
infuraBody = new InfuraBody(method, new Object[]{new InfuraBody.EthCallParams("0x70a08231000000000000000000000000" + address, contract), "latest"}, id);
break;
case ROOTSTOCK_ETH_SEND_RAW_TRANSACTION:
infuraBody = new InfuraBody(method, new String[]{tx}, id);
break;
case ROOTSTOCK_ETH_GAS_PRICE:
infuraBody = new InfuraBody(method, id);
break;
default:
infuraBody = new InfuraBody();
}
Call<InfuraResponse> call = rootstockApi.rootstock(infuraBody);
call.enqueue(new Callback<InfuraResponse>() {
@Override
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
requestsCount--;
if (response.code() == 200) {
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
requestsCount--;
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}
}

View file

@ -1,186 +0,0 @@
package com.tangem.data.network;
import android.util.Log;
import androidx.annotation.NonNull;
import com.tangem.App;
import com.tangem.data.Blockchain;
import com.tangem.data.network.model.SoChain;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiSoChain {
private static String TAG = ServerApiSoChain.class.getSimpleName();
private int requestsCount = 0;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
public interface AddressInfoListener {
void onSuccess(SoChain.Response.AddressBalance response);
void onSuccess(SoChain.Response.TxUnspent response);
void onFail(String message);
}
private AddressInfoListener addressInfoListener;
public void setAddressInfoListener(AddressInfoListener listener) {
addressInfoListener = listener;
}
public interface SendTxListener {
void onSuccess(SoChain.Response.SendTx response);
void onFail(String message);
}
private SendTxListener sendTxListener;
public void setSendTxListener(SendTxListener listener) {
sendTxListener = listener;
}
public interface TransactionInfoListener {
void onSuccess(SoChain.Response.GetTx response);
void onFail(String message);
}
private TransactionInfoListener txInfoListener;
public void setTransactionInfoListener(TransactionInfoListener listener) {
txInfoListener=listener;
}
private String getNetwork(Blockchain blockchain) throws Exception {
switch (blockchain) {
case Bitcoin:
return "BTC";
case BitcoinTestNet:
return "BTCTEST";
case Litecoin:
return "LTC";
default:
throw new Exception("SoChainAPI don't support blockchain " + blockchain.getID());
}
}
public void requestAddressBalance(Blockchain blockchain, String wallet) throws Exception {
requestsCount++;
SoChainApi api = App.Companion.getNetworkComponent().getRetrofitSoChain().create(SoChainApi.class);
Call<SoChain.Response.AddressBalance> call = api.getAddressBalance(getNetwork(blockchain), wallet);
call.enqueue(new Callback<SoChain.Response.AddressBalance>() {
@Override
public void onResponse(@NonNull Call<SoChain.Response.AddressBalance> call, @NonNull Response<SoChain.Response.AddressBalance> response) {
requestsCount--;
Log.i(TAG, "requestAddressBalance onResponse " + response.code());
if (response.code() == 200) {
addressInfoListener.onSuccess(response.body());
} else {
addressInfoListener.onFail(String.valueOf(response.code()));
}
}
@Override
public void onFailure(@NonNull Call<SoChain.Response.AddressBalance> call, @NonNull Throwable t) {
requestsCount--;
Log.e(TAG, "requestAddressBalance onFailure " + t.getMessage());
addressInfoListener.onFail(String.valueOf(t.getMessage()));
}
});
}
public void requestUnspentTx(Blockchain blockchain, String wallet) throws Exception {
requestsCount++;
SoChainApi api = App.Companion.getNetworkComponent().getRetrofitSoChain().create(SoChainApi.class);
Call<SoChain.Response.TxUnspent> call = api.getUnspentTx(getNetwork(blockchain), wallet);
call.enqueue(new Callback<SoChain.Response.TxUnspent>() {
@Override
public void onResponse(@NonNull Call<SoChain.Response.TxUnspent> call, @NonNull Response<SoChain.Response.TxUnspent> response) {
requestsCount--;
Log.i(TAG, "requestAddressBalance onResponse " + response.code());
if (response.code() == 200) {
addressInfoListener.onSuccess(response.body());
} else {
addressInfoListener.onFail(String.valueOf(response.code()));
}
}
@Override
public void onFailure(@NonNull Call<SoChain.Response.TxUnspent> call, @NonNull Throwable t) {
requestsCount--;
Log.e(TAG, "requestAddressBalance onFailure " + t.getMessage());
addressInfoListener.onFail(String.valueOf(t.getMessage()));
}
});
}
public void requestSendTransaction(Blockchain blockchain, String txHEX) throws Exception {
requestsCount++;
SoChainApi api = App.Companion.getNetworkComponent().getRetrofitSoChain().create(SoChainApi.class);
SoChain.Request.SendTx tx=new SoChain.Request.SendTx();
tx.setTx_hex(txHEX);
Call<SoChain.Response.SendTx> call = api.sendTransaction(getNetwork(blockchain), tx);
call.enqueue(new Callback<SoChain.Response.SendTx>() {
@Override
public void onResponse(@NonNull Call<SoChain.Response.SendTx> call, @NonNull Response<SoChain.Response.SendTx> response) {
requestsCount--;
Log.i(TAG, "requestAddressBalance onResponse " + response.code());
if (response.code() == 200) {
sendTxListener.onSuccess(response.body());
} else {
sendTxListener.onFail(String.valueOf(response.code()));
}
}
@Override
public void onFailure(@NonNull Call<SoChain.Response.SendTx> call, @NonNull Throwable t) {
requestsCount--;
Log.e(TAG, "requestAddressBalance onFailure " + t.getMessage());
sendTxListener.onFail(String.valueOf(t.getMessage()));
}
});
}
public void requestTransactionInfo(Blockchain blockchain, String txId) throws Exception {
requestsCount++;
SoChainApi api = App.Companion.getNetworkComponent().getRetrofitSoChain().create(SoChainApi.class);
Call<SoChain.Response.GetTx> call = api.getTx(getNetwork(blockchain), txId);
call.enqueue(new Callback<SoChain.Response.GetTx>() {
@Override
public void onResponse(@NonNull Call<SoChain.Response.GetTx> call, @NonNull Response<SoChain.Response.GetTx> response) {
requestsCount--;
Log.i(TAG, "requestAddressBalance onResponse " + response.code());
if (response.code() == 200) {
txInfoListener.onSuccess(response.body());
} else {
txInfoListener.onFail(String.valueOf(response.code()));
}
}
@Override
public void onFailure(@NonNull Call<SoChain.Response.GetTx> call, @NonNull Throwable t) {
requestsCount--;
Log.e(TAG, "requestAddressBalance onFailure " + t.getMessage());
txInfoListener.onFail(String.valueOf(t.getMessage()));
}
});
}
}

View file

@ -1,206 +0,0 @@
package com.tangem.data.network;
import com.tangem.App;
import com.tangem.data.Blockchain;
import com.tangem.util.LOG;
import com.tangem.wallet.R;
import com.tangem.wallet.TangemContext;
import org.stellar.sdk.Network;
import org.stellar.sdk.Server;
import org.stellar.sdk.requests.ErrorResponse;
import java.io.IOException;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DefaultObserver;
import io.reactivex.schedulers.Schedulers;
/**
* Created by dvol on 7.01.2019.
* <p>
* Request processor for Stellar Horizon Rest Api
* Every request live cycle:
* 1. In application create request and call {@link ServerApiStellar}.requestData(..)
* 2. Try send every request for max 4 times,
* 3. If all 4 times fail call DefaultObserver<StellarRequest>.onError (defined in .requestData(..)) and than
* {@link Listener}.onFail(...) callback
* Error can be acquired with {@link StellarRequest}.getError() method
* 4. If request network communication finished successfully then call DefaultObserver<StellarRequest.Base>.onComplete (defined in .requestData) and than
* {@link Listener}.onSuccess(...) callback
*/
public class ServerApiStellar {
public ServerApiStellar(Blockchain blockchain) {
if (blockchain == Blockchain.Stellar || blockchain == Blockchain.StellarAsset || blockchain == Blockchain.StellarTag) {
currentURL = ServerURL.API_STELLAR;
} else {
currentURL = ServerURL.API_STELLAR_TESTNET;
}
}
private static String TAG = ServerApiStellar.class.getSimpleName();
/**
* TCP, SSL
* Used in BTC, BCH
*/
private Listener listener;
private int requestsCount = 0;
private String currentURL;
public String getCurrentURL() {
return currentURL;
}
public boolean isRequestsSequenceCompleted() {
LOG.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
/**
* Interface for notification every request result
*/
public interface Listener {
/**
* Notify that request processing was successful
*
* @param stellarRequest - processed request containing received answer {@see stellarRequest.getAnswer() method}
*/
void onSuccess(StellarRequest.Base stellarRequest);
/**
* Notify that request processing was successful
*
* @param stellarRequest - processed request containing occurred error {@see stellarRequest.getError() method}
*/
void onFail(StellarRequest.Base stellarRequest);
}
/**
* Set notificaion listener
*
* @param listener
*/
public void setListener(Listener listener) {
this.listener = listener;
}
/**
* Start process request
*
* @param ctx
* @param stellarRequest
*/
public void requestData(TangemContext ctx, StellarRequest.Base stellarRequest) {
requestData(ctx, stellarRequest, false);
}
public void requestData(TangemContext ctx, StellarRequest.Base stellarRequest, boolean isRetry) {
requestsCount++;
LOG.i(TAG, String.format("New request[%d]: %s", requestsCount, stellarRequest.getClass().getSimpleName()));
Observable<StellarRequest.Base> stellarObserver = Observable.just(stellarRequest)
.doOnEach(stellarRequest1 -> doStellarRequest(ctx, stellarRequest))
.flatMap(stellarRequest1 -> {
if (stellarRequest1.errorResponse != null) {
LOG.e(TAG, "Error response on " + stellarRequest.getClass().getSimpleName());
return Observable.error(stellarRequest.errorResponse);
} else
return Observable.just(stellarRequest1);
}
)
// .retryWhen(errors -> errors
// .filter(throwable -> (throwable instanceof IOException) || (throwable instanceof ErrorResponse))
// .zipWith(Observable.range(1, 4), (n, i) -> i))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
stellarObserver.subscribe(new DefaultObserver<StellarRequest.Base>() {
@Override
public void onNext(StellarRequest.Base stellarRequest) {
LOG.e(TAG, "requestData " + stellarRequest.getClass().getSimpleName() + " onNext ");
}
@Override
public void onError(Throwable e) {
requestsCount--;
LOG.e(TAG, "requestData " + stellarRequest.getClass().getSimpleName() + " onError " + e.getMessage());
LOG.e(TAG, String.format("%d requests left in processing", requestsCount));
if (isRetry || (stellarRequest.errorResponse != null && stellarRequest.errorResponse.getCode() == 404)) {
stellarRequest.setError(e.getMessage());
//setErrorOccurred(e.getMessage());//;
listener.onFail(stellarRequest);
} else {
retryRequest(ctx, stellarRequest);
}
}
/**
* Called after completion request processing
*/
@Override
public void onComplete() {
requestsCount--;
LOG.e(TAG, String.format("%d requests left in processing", requestsCount));
if (stellarRequest.getError() != null) {
LOG.i(TAG, "requestData " + stellarRequest.getClass().getSimpleName() + " onComplete, error!=null");
if (isRetry || (stellarRequest.errorResponse != null && stellarRequest.errorResponse.getCode() == 404)) {
listener.onFail(stellarRequest);
} else {
retryRequest(ctx, stellarRequest);
}
} else {
LOG.e(TAG, "requestData " + stellarRequest.getClass().getSimpleName() + " onComplete, error==null");
listener.onSuccess(stellarRequest);
}
}
});
}
public void doStellarRequest(TangemContext ctx, StellarRequest.Base stellarRequest) throws IOException {
stellarRequest.setError(null);
try {
Server server;
Blockchain blockchain = ctx.getBlockchain();
if (blockchain == Blockchain.Stellar || blockchain == Blockchain.StellarAsset || blockchain == Blockchain.StellarTag) {
Network.usePublicNetwork();
server = new Server(currentURL);
} else if (blockchain == Blockchain.StellarTestNet) {
Network.useTestNetwork();
server = new Server(currentURL);
} else {
throw new IOException("Wrong blockchain for ServerApiStellar");
}
try {
LOG.e(TAG, "--- request " + stellarRequest.getClass().getSimpleName());
stellarRequest.process(server);
} catch (ErrorResponse errorResponse) {
LOG.e(TAG, "--- error response: " + errorResponse.getMessage());
stellarRequest.errorResponse = errorResponse;
stellarRequest.setError(errorResponse.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
stellarRequest.setError(App.Companion.getInstance().getString(R.string.loaded_wallet_error_blockchain_communication_error));
throw e;
}
}
public void retryRequest (TangemContext ctx, StellarRequest.Base stellarRequest) {
currentURL = ServerURL.API_STELLAR_RESERVE;
requestData(ctx, stellarRequest, true);
}
}

View file

@ -1,139 +0,0 @@
package com.tangem.data.network;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.data.network.model.TezosAccountResponse;
import com.tangem.data.network.model.TezosForgeBody;
import com.tangem.data.network.model.TezosHeaderResponse;
import com.tangem.data.network.model.TezosPreapplyBody;
import com.tangem.tangem_card.util.Log;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Single;
import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class ServerApiTezos {
private static String TAG = ServerApiTezos.class.getSimpleName();
private final String letzbakeURI = "https://teznode.letzbake.com";
private final String tezrpcURI = "https://mainnet.tezrpc.me";
static final String TEZOS_ADDRESS = "chains/main/blocks/head/context/contracts/{address}";
static final String TEZOS_HEADER = "chains/main/blocks/head/header";
static final String TEZOS_MANAGER_KEY = "chains/main/blocks/head/context/contracts/{address}/manager_key";
static final String TEZOS_FORGE_OPERATIONS = "chains/main/blocks/head/helpers/forge/operations";
static final String TEZOS_PREAPPLY_OPERATIONS = "chains/main/blocks/head/helpers/preapply/operations";
static final String TEZOS_RUN_OPERATION = "chains/main/blocks/head/helpers/scripts/run_operation";
static final String TEZOS_INJECT_OPERATIONS = "injection/operation";
private Retrofit retrofitTezos = new Retrofit.Builder()
.baseUrl(letzbakeURI)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
//logging for testing
.client(new OkHttpClient.Builder().addInterceptor(
new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
).build())
.build();
private TezosApi tezosApi = retrofitTezos.create(TezosApi.class);
private int requestsCount = 0;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
public void getAddress(String wallet, SingleObserver<TezosAccountResponse> accountObserver) {
requestsCount++;
Log.i(TAG, "new getAddress request");
Single<TezosAccountResponse> accountSingle = tezosApi.getAccount(wallet)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((object, throwable) -> requestsCount--);
accountSingle.subscribe(accountObserver);
}
public void getMangerKey(String wallet, SingleObserver<String> accountObserver) {
requestsCount++;
Log.i(TAG, "new getManagerKey request");
Single<String> managerKeySingle = tezosApi.getManagerKey(wallet)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((object, throwable) -> requestsCount--);
managerKeySingle.subscribe(accountObserver);
}
public TezosHeaderResponse getHeader() throws Exception { // TODO? not async
requestsCount++;
Log.i(TAG, "new getHeader request");
Response<TezosHeaderResponse> headerResponse = tezosApi.getHeader().execute();
requestsCount--;
if (headerResponse.code() == 200) {
return headerResponse.body();
} else {
throw new Exception("Wrong header response, code: " + headerResponse.code());
}
}
public String forgeOperations(TezosForgeBody tezosForgeBody) throws Exception { // TODO? not async
requestsCount++;
Log.i(TAG, "new forgeOperations request");
Response<String> forgeResponse = tezosApi.forgeOperations(tezosForgeBody).execute();
requestsCount--;
if (forgeResponse.code() == 200) {
return forgeResponse.body();
} else {
throw new Exception("Wrong forge response, code: " + forgeResponse.code());
}
}
public void peapplyOperations(TezosPreapplyBody tezosPreapplyBody) throws Exception {
Log.i(TAG, "new peapplyOperations request");
List<TezosPreapplyBody> tezosPreapplyBodyList = new ArrayList<>();
tezosPreapplyBodyList.add(tezosPreapplyBody);
Response<Void> preapplyResponse = tezosApi.preapplyOperations(tezosPreapplyBodyList).execute();
if (preapplyResponse.code() != 200) {
String error = "Preapply error: unknown error";
if (preapplyResponse.errorBody() != null) {
error = "Preapply error: " + preapplyResponse.errorBody().string();
}
Log.e(TAG, error);
throw new Exception(error);
}
}
public void injectOperations(String txForSend, SingleObserver<Object> injectObserver) {
requestsCount++;
Log.i(TAG, "new injectOperations request");
Single<Object> injectSingle = tezosApi.injectOperations(txForSend)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((object, throwable) -> requestsCount--);
injectSingle.subscribe(injectObserver);
}
}

View file

@ -1,53 +0,0 @@
package com.tangem.data.network
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import com.tangem.data.network.model.TokenEmvGetTransferFeeAnswer
import com.tangem.data.network.model.TokenEmvGetTransferFeeBody
import com.tangem.data.network.model.TokenEmvTransferAnswer
import com.tangem.data.network.model.TokenEmvTransferBody
import com.tangem.tangem_card.util.Log
import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.scalars.ScalarsConverterFactory
import java.util.concurrent.TimeUnit
class ServerApiTokenEmv {
private val TAG = ServerApiTokenEmv::class.java.simpleName
private val tangemServer = "https://emvsupport.appspot.com/"
private val tokenEmvApi = Retrofit.Builder()
.baseUrl(tangemServer)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //logging for testing
.client(OkHttpClient.Builder().addInterceptor(
HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
).build())
.build()
.create(TokenEmvApi::class.java)
fun transfer(tokenEmvTransferBody: TokenEmvTransferBody, transferObserver: SingleObserver<TokenEmvTransferAnswer>) {
Log.i(TAG, "new transfer request")
tokenEmvApi.transfer(tokenEmvTransferBody)
.timeout(30, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(transferObserver)
}
fun getTransferFee(tokenEmvGetTransferFeeBody: TokenEmvGetTransferFeeBody, transferObserver: SingleObserver<TokenEmvGetTransferFeeAnswer>) {
Log.i(TAG, "new get transfer fee request")
tokenEmvApi.getTransferFee(tokenEmvGetTransferFeeBody)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(transferObserver)
}
}

View file

@ -1,23 +0,0 @@
package com.tangem.data.network;
class ServerURL {
static final String API_TANGEM = "https://verify.tangem.com/";
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/";
static final String API_ROOTSTOCK = "https://public-node.rsk.co/";
static final String API_BLOCKCYPHER = "https://api.blockcypher.com/";
static final String API_BINANCE = "https://dex.binance.org/";
static final String API_BINANCE_TESTNET = "https://testnet-dex.binance.org/";
static final String API_MATIC_TESTNET = "https://testnet2.matic.network/";
static final String API_STELLAR = "https://horizon.stellar.org/";
static final String API_STELLAR_RESERVE = "https://horizon.sui.li/";
static final String API_STELLAR_TESTNET = "https://horizon-testnet.stellar.org/";
static final String API_BLOCKCHAIN_INFO = "https://blockchain.info/";
static final String API_DUCATUS = "https://ducapi.rocknblock.io/";
static final String API_BLOCKCHAIR = "https://api.blockchair.com/";
}

View file

@ -1,26 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.SoChain;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface SoChainApi {
@GET(Server.ApiSoChain.Method.ADDRESS_BALANCE)
Call<SoChain.Response.AddressBalance> getAddressBalance(@Path("network") String network, @Path("address") String address);
@GET(Server.ApiSoChain.Method.UNSPENT_TX)
Call<SoChain.Response.TxUnspent> getUnspentTx(@Path("network") String network, @Path("address") String address);
@GET(Server.ApiSoChain.Method.GET_TX)
Call<SoChain.Response.GetTx> getTx(@Path("network") String network, @Path("txid") String txId);
@Headers("Content-Type: application/json")
@POST(Server.ApiSoChain.Method.SEND_TRANSACTION)
Call<SoChain.Response.SendTx> sendTransaction(@Path("network") String network, @Body SoChain.Request.SendTx body);
}

View file

@ -1,113 +0,0 @@
package com.tangem.data.network;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.Server;
import org.stellar.sdk.Transaction;
import org.stellar.sdk.requests.ErrorResponse;
import org.stellar.sdk.requests.RequestBuilder;
import org.stellar.sdk.responses.AccountResponse;
import org.stellar.sdk.responses.LedgerResponse;
import org.stellar.sdk.responses.Page;
import org.stellar.sdk.responses.SubmitTransactionResponse;
import org.stellar.sdk.responses.operations.OperationResponse;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
/**
* Created by dvol on 7.01.2019.
*/
public class StellarRequest {
public static abstract class Base {
public ErrorResponse errorResponse;
private String error = null;
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public abstract void process(Server server) throws IOException;
}
public static class Balance extends Base {
KeyPair accountKeyPair;
public AccountResponse accountResponse;
public Balance(String walletAddress) {
accountKeyPair = KeyPair.fromAccountId(walletAddress);
}
@Override
public void process(Server server) throws IOException {
accountResponse = server.accounts().account(accountKeyPair);
}
}
public static class SubmitTransaction extends Base {
public Transaction transaction;
public SubmitTransactionResponse response;
public SubmitTransaction(Transaction transaction) {
this.transaction = transaction;
}
@Override
public void process(Server server) throws IOException {
// // First, check to make sure that the destination account exists.
// // You could skip this, but if the account does not exist, you will be charged
// // the transaction fee when the transaction fails.
// // It will throw HttpResponseException if account does not exist or there was another error.
// server.accounts().account(targetAccount);
//
// // If there was no error, load up-to-date information on your account.
// AccountResponse sourceAccount = server.accounts().account(sourceAccount);
// And finally, send it off to Stellar!
response = server.submitTransaction(transaction);
}
}
public static class Ledgers extends Base {
public LedgerResponse ledgerResponse;
public Ledgers() {
}
@Override
public void process(Server server) throws IOException {
int latestLedger = server.root().getHistoryLatestLedger();
ledgerResponse = server.ledgers().ledger(latestLedger);
}
}
public static class Operations extends Base {
KeyPair accountKeyPair;
public List<OperationResponse> operationsList;
int limit = 200;
public Operations(String walletAddress) {
accountKeyPair = KeyPair.fromAccountId(walletAddress);
}
@Override
public void process(Server server) throws IOException {
Page<OperationResponse> operationsResponse = server.operations().forAccount(accountKeyPair).limit(limit).order(RequestBuilder.Order.DESC).execute();
operationsList = operationsResponse.getRecords();
while (operationsResponse.getRecords().size() == limit) {
try {
operationsResponse = operationsResponse.getNextPage(server.getHttpClient());
operationsList.addAll(operationsResponse.getRecords());
} catch (URISyntaxException e) {
break;
}
}
}
}
}

View file

@ -1,35 +0,0 @@
package com.tangem.data.network;
import com.tangem.data.network.model.TezosAccountResponse;
import com.tangem.data.network.model.TezosForgeBody;
import com.tangem.data.network.model.TezosHeaderResponse;
import com.tangem.data.network.model.TezosPreapplyBody;
import java.util.List;
import io.reactivex.Single;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface TezosApi {
@GET(ServerApiTezos.TEZOS_ADDRESS)
Single<TezosAccountResponse> getAccount(@Path("address") String address);
@GET(ServerApiTezos.TEZOS_HEADER)
Call<TezosHeaderResponse> getHeader();
@GET(ServerApiTezos.TEZOS_MANAGER_KEY)
Single<String> getManagerKey(@Path("address") String address);
@POST(ServerApiTezos.TEZOS_FORGE_OPERATIONS)
Call<String> forgeOperations(@Body TezosForgeBody tezosForgeBody);
@POST(ServerApiTezos.TEZOS_PREAPPLY_OPERATIONS)
Call<Void> preapplyOperations(@Body List<TezosPreapplyBody> tezosPreapplyBodyList);
@POST(ServerApiTezos.TEZOS_INJECT_OPERATIONS)
Single<Object> injectOperations(@Body String txForSend);
}

View file

@ -1,17 +0,0 @@
package com.tangem.data.network
import com.tangem.data.network.model.TokenEmvGetTransferFeeAnswer
import com.tangem.data.network.model.TokenEmvGetTransferFeeBody
import com.tangem.data.network.model.TokenEmvTransferAnswer
import com.tangem.data.network.model.TokenEmvTransferBody
import io.reactivex.Completable
import io.reactivex.Single
import retrofit2.http.Body
import retrofit2.http.POST
interface TokenEmvApi {
@POST("./card/transfer")
fun transfer(@Body tokenEmvTransferBody: TokenEmvTransferBody): Single<TokenEmvTransferAnswer>
@POST("./card/transfer/fee")
fun getTransferFee(@Body tokenEmvGetTransferFeeBody: TokenEmvGetTransferFeeBody): Single<TokenEmvGetTransferFeeAnswer>
}

View file

@ -1,10 +0,0 @@
package com.tangem.data.network;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.GET;
public interface UpdateVersionApi {
@GET(Server.ApiUpdateVersion.Method.LAST_VERSION)
Call<ResponseBody> getLastVersion();
}

View file

@ -1,31 +0,0 @@
/**
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tangem.data.network.exception
/**
* Base Class for handling errors/failures/exceptions.
* Every feature specific failure should extend [FeatureFailure] class.
*/
sealed class Failure {
class ApplicationError(val message: String) : Failure()
class NotFound : FeatureFailure()
class NetworkConnection : Failure()
class ServerError : Failure()
class Unauthorized : Failure() // 401
/** * Extend this class for feature specific failures.*/
abstract class FeatureFailure : Failure()
}

View file

@ -1,9 +0,0 @@
package com.tangem.data.network.model;
public class AdaliteBody {
private String signedTx;
public AdaliteBody(String signedTx) {
this.signedTx = signedTx;
}
}

View file

@ -1,45 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class AdaliteResponse(
@SerializedName("Right")
var right: AdaliteAddressData? = null
)
data class AdaliteResponseUtxo(
@SerializedName("Right")
var right: List<AdaliteUtxoData>
)
data class AdaliteAddressData(
@SerializedName("caAddress")
var caAddress: String? = null,
@SerializedName("caBalance")
var caBalance: AdaliteCoins? = null,
@SerializedName("caTxList")
var caTxList: List<AdaliteTxData>
)
data class AdaliteCoins(
@SerializedName("getCoin")
var getCoin: Long? = null
)
data class AdaliteUtxoData(
@SerializedName("cuId")
var cuId: String? = null,
@SerializedName("cuOutIndex")
var cuOutIndex: Int? = null,
@SerializedName("cuCoins")
var cuCoins: AdaliteCoins? = null
)
data class AdaliteTxData(
@SerializedName("ctbId")
var ctbId: String? = null
)

View file

@ -1,16 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class BinanceFees(
@SerializedName("fixed_fee_params")
var fixed_fee_params: BinanceFixedFee? = null
)
data class BinanceFixedFee(
@SerializedName("msg_type")
var msg_type: String? = null,
@SerializedName("fee")
var fee: Int? = null
)

View file

@ -1,34 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class BitcoreBalance(
@SerializedName("confirmed")
var confirmed: Long? = null,
@SerializedName("unconfirmed")
var unconfirmed: Long? = null
)
data class BitcoreUtxo(
@SerializedName("mintTxid")
var mintTxid: String? = null,
@SerializedName("mintIndex")
var mintIndex: Int? = null,
@SerializedName("value")
var value: Long? = null,
@SerializedName("script")
var script: String? = null
)
data class BitcoreBalanceAndUnspents(
var balance: BitcoreBalance,
var unspents: List<BitcoreUtxo>
)
data class BitcoreSendResponse(
var txid: String? = null
)

View file

@ -1,14 +0,0 @@
package com.tangem.data.network.model;
import java.util.ArrayList;
import java.util.List;
public class BitcoreSendBody {
private List<String> rawTx;
public BitcoreSendBody(String tx) {
List<String> txList = new ArrayList<>();
txList.add(tx);
rawTx = txList;
}
}

View file

@ -1,56 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class BlockchainInfoAddress(
@SerializedName("final_balance")
var final_balance: Long? = null,
@SerializedName("txs")
var txs: List<BlockchainInfoTransaction>? = null
)
data class BlockchainInfoTransaction(
@SerializedName("hash")
var hash: String? = null,
@SerializedName("block_height")
var block_height: Long? = null,
@SerializedName("inputs")
var inputs: List<BlockchainInfoInput>
)
data class BlockchainInfoUnspents(
@SerializedName("unspent_outputs")
var unspent_outputs: List<BlockchainInfoUtxo>
)
data class BlockchainInfoUtxo(
@SerializedName("tx_hash_big_endian")
var tx_hash_big_endian: String? = null,
@SerializedName("tx_output_n")
var tx_output_n: Int? = null,
@SerializedName("value")
var value: Long? = null,
@SerializedName("script")
var script: String? = null
)
data class BlockchainInfoInput(
@SerializedName("prev_out")
var prev_out: BlockchainInfoOutput
)
data class BlockchainInfoOutput(
@SerializedName("addr")
var addr: String? = null
)
data class BlockchainInfoAddressAndUnspents(
var address: BlockchainInfoAddress,
var unspents: BlockchainInfoUnspents
)

View file

@ -1,69 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class BlockchairAddressResponse(
@SerializedName("data")
val data: Map<String, BlockchairAddressData>? = null
)
data class BlockchairAddressData(
@SerializedName("address")
val address: BlockchairAddressInfo? = null,
@SerializedName("utxo")
val unspentOutputs: List<BlockchairUnspentOutput>? = null,
@SerializedName("transactions")
val transactions: List<String>
)
data class BlockchairAddressInfo(
@SerializedName("balance")
val balance: Long? = null,
@SerializedName("output_count")
val outputCount: Int? = null,
@SerializedName("unspent_output_count")
val unspentOutputCount: Int? = null
)
data class BlockchairUnspentOutput(
@SerializedName("block_id")
val block: Int? = null,
@SerializedName("transaction_hash")
val transactionHash: String? = null,
@SerializedName("index")
val index: Int? = null,
@SerializedName("value")
val amount: Long? = null
)
data class BlockchairTransactionResponse(
@SerializedName("data")
val data: Map<String, BlockchairTransactionData>? = null
)
data class BlockchairTransactionData(
@SerializedName("transaction")
val transaction: BlockchairTransactionInfo? = null
)
data class BlockchairTransactionInfo(
@SerializedName("block_id")
val block: Int? = null
)
data class BlockchairStatsResponse(
@SerializedName("data")
val data: BlockchairStatsData? = null
)
data class BlockchairStatsData(
@SerializedName("suggested_transaction_fee_per_byte_sat")
val feePerByte: Int? = null
)

View file

@ -1,9 +0,0 @@
package com.tangem.data.network.model;
public class BlockchairSendBody {
private String data;
public BlockchairSendBody(String data) {
this.data = data;
}
}

View file

@ -1,9 +0,0 @@
package com.tangem.data.network.model;
public class BlockcypherBody {
private String tx;
public BlockcypherBody(String tx) {
this.tx = tx;
}
}

View file

@ -1,65 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class BlockcypherResponse(
@SerializedName("address")
var address: String? = null,
@SerializedName("balance")
var balance: Long? = null,
@SerializedName("unconfirmed_balance")
var unconfirmed_balance: Long? = null,
@SerializedName("txrefs")
var txrefs: List<BlockcypherTxref>? = null,
@SerializedName("unconfirmed_txrefs")
var unconfirmed_txrefs: List<BlockcypherTxref>? = null,
@SerializedName("hasMore")
var hasMore: Boolean? = null
)
data class BlockcypherTxref(
@SerializedName("tx_hash")
var tx_hash: String? = null,
@SerializedName("tx_input_n")
var tx_input_n: Int? = null,
@SerializedName("tx_output_n")
var tx_output_n: Int? = null,
@SerializedName("value")
var value: Long? = null,
@SerializedName("confirmations")
var confirmations: Long? = null,
@SerializedName("script")
var script: String? = null,
@SerializedName("spent")
var spent: Boolean? = null
)
data class BlockcypherTx(
@SerializedName("hex")
var hex: String? = null,
@SerializedName("addresses")
var addesses: List<String>? = null
)
data class BlockcypherFee(
@SerializedName("low_fee_per_kb")
var low_fee_per_kb: Long? = null,
@SerializedName("medium_fee_per_kb")
var medium_fee_per_kb: Long? = null,
@SerializedName("high_fee_per_kb")
var high_fee_per_kb: Long? = null
)

View file

@ -1,42 +0,0 @@
package com.tangem.data.network.model;
public class InfuraBody {
private String method;
private Object[] params;
private int id;
private String jsonrpc = "2.0";
public InfuraBody() {
}
// body for eth gasPrice
public InfuraBody(String method, int id) {
this.method = method;
this.id = id;
}
// body for eth getBalance, eth getTransactionCount, eth sendRawTransaction
public InfuraBody(String method, String[] params, int id) {
this.method = method;
this.params = params;
this.id = id;
}
// body for eth call
public InfuraBody(String method, Object[] params, int id) {
this.method = method;
this.params = params;
this.id = id;
}
public static class EthCallParams {
private String data;
private String to;
public EthCallParams(String data, String to) {
this.data = data;
this.to = to;
}
}
}

View file

@ -1,17 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class InfuraResponse(
@SerializedName("jsonrpc")
var jsonrpc: String = "",
@SerializedName("id")
var id: Int? = null,
@SerializedName("result")
var result: String? = null,
@SerializedName("error")
var error: Object
)

View file

@ -1,9 +0,0 @@
package com.tangem.data.network.model;
public class InsightBody {
private String rawtx;
public InsightBody(String rawtx){
this.rawtx = rawtx;
}
}

View file

@ -1,40 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class InsightResponse(
@SerializedName("balanceSat")
var balanceSat: Long? = null,
@SerializedName("unconfirmedBalanceSat")
var unconfirmedBalanceSat: Long? = null,
@SerializedName("addrStr")
var addrStr: String = "",
// @SerializedName("2")
// var fee2: String = "",
//
// @SerializedName("3")
// var fee3: String = "",
//
// @SerializedName("6")
// var fee6: String = "",
@SerializedName("error")
var error: String = ""
)
data class InsightUtxo(
@SerializedName("txid")
var txid: String = "",
@SerializedName("satoshis")
var satoshis: Long? = null,
@SerializedName("vout")
var vout: Int? = null,
@SerializedName("scriptPubKey")
var scriptPubKey: String? = null
)

View file

@ -1,27 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class PayIdResponse(
@SerializedName("addresses")
var addresses: List<PayIdAddress>? = null
)
data class PayIdAddress(
@SerializedName("paymentNetwork")
var paymentNetwork: String? = null,
@SerializedName("environment")
var environment: String? = null,
@SerializedName("addressDetails")
var addressDetails: PayIdAddressDetails? = null
)
data class PayIdAddressDetails(
@SerializedName("address")
var address: String? = null,
@SerializedName("tag")
var tag: String? = null
)

View file

@ -1,23 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class RateInfoResponse(
@SerializedName("data")
var data: RateData? = null
)
data class RateData(
@SerializedName("quote")
var quote: Quote? = null
)
data class Quote(
@SerializedName("USD")
var usd: CurrencyRate? = null
)
data class CurrencyRate(
@SerializedName("price")
var price: Float? = null
)

View file

@ -1,24 +0,0 @@
package com.tangem.data.network.model;
import java.util.ArrayList;
import java.util.HashMap;
public class RippleBody {
private String method;
private ArrayList<HashMap<String,String>> params;
public RippleBody() {
}
//for RIPPLE_FEE
public RippleBody(String method) {
this.method = method;
}
public RippleBody(String method, HashMap<String,String> paramsMap) {
this.method = method;
ArrayList<HashMap<String, String>> paramsList = new ArrayList<>();
paramsList.add(paramsMap);
this.params = paramsList;
}
}

View file

@ -1,78 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class RippleResponse(
@SerializedName("result")
var result: RippleResult? = null
)
data class RippleResult(
@SerializedName("account_data")
var account_data: RippleAccountData? = null,
@SerializedName("validated")
var validated: Boolean? = null,
//for RIPPLE_FEE
@SerializedName("drops")
var drops: RippleFeeDrops? = null,
//for RIPPLE_SUBMIT
@SerializedName("engine_result_code")
var engine_result_code: Int? = null,
//for RIPPLE_SUBMIT
@SerializedName ("engine_result_message")
var engine_result_message: String? = null,
//for RIPPLE_SUBMIT
@SerializedName("error")
var error: String? = null,
//for RIPPLE_SUBMIT
@SerializedName("error_exception")
var error_exception: String? = null,
//for RIPPLE_SERVER_STATE
@SerializedName("state")
var state: RippleState? = null,
//for "Account not found error"
@SerializedName("error_code")
var error_code: Int? = null
)
data class RippleAccountData(
@SerializedName("Account")
var account: String? = null,
@SerializedName("Balance")
var balance: String? = null,
@SerializedName("Sequence")
var sequence: Long? = null
)
data class RippleFeeDrops(
//enough to put tx to queue
@SerializedName("minimum_fee")
var minimum_fee: String? = null,
//enough to put tx to current ledger
@SerializedName("open_ledger_fee")
var open_ledger_fee: String? = null,
@SerializedName("median_fee")
var median_fee: String? = null
)
data class RippleState(
@SerializedName("validated_ledger")
var validated_ledger: RippleLedger? = null
)
data class RippleLedger(
@SerializedName("reserve_base")
var reserve_base: Long? = null
)

View file

@ -1,70 +0,0 @@
package com.tangem.data.network.model
class SoChain {
class Request {
class SendTx{
var tx_hex: String? = null
}
}
class Response {
class AddressBalance {
class Data {
var network: String? = null
var address: String? = null
var confirmed_balance: String? = null
var unconfirmed_balance: String? = null
var confirmations: String? = null
}
var status: String? = null
var data: Data? = null
}
class TxUnspent {
class Data {
class Tx {
var txid: String? = null //"9b5c8fbeb1e42bb2a6da40e2eab49c368d1a205707a1ec88aa13f0f2ecdfe944",
var output_no: Int? = null // 0,
var script_asm: String? = null // "OP_DUP OP_HASH160 8541eb0593bb19c3755198e7d2a71e134da21a97 OP_EQUALVERIFY OP_CHECKSIG",
var script_hex: String? = null // "76a9148541eb0593bb19c3755198e7d2a71e134da21a9788ac",
var value: String? = null // "11.38404832",
var confirmations: Long? = null
var time: Long? = null// : 1555509495
}
var network: String? = null
var address: String? = null
var txs: Array<Tx>? = null
}
var status: String? = null
var data: Data? = null
}
class SendTx {
class Data {
var network: String? = null
var txid: String? = null
var tx_hex: String? = null
}
var status: String? = null
var data: Data? = null
}
class GetTx {
class Data {
// restricted data
var network: String? = null
var address: String? = null
var txid: String? = null
var tx_hex: String? = null
}
var status: String? = null
var data: Data? = null
}
}
}

View file

@ -1,25 +0,0 @@
package com.tangem.data.network.model
data class TezosForgeBody(
val branch: String,
val contents: List<TezosOperationContent>
)
data class TezosOperationContent(
val kind: String,
val source: String,
val fee: String,
val counter: String,
val gas_limit: String,
val storage_limit: String,
val public_key: String? = null,
val destination: String? = null,
val amount: String? = null
)
data class TezosPreapplyBody(
val protocol: String,
val branch: String,
val contents: List<TezosOperationContent>,
val signature: String
)

View file

@ -1,19 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class TezosAccountResponse(
@SerializedName("balance")
var balance: Long? = null,
@SerializedName("counter")
var counter: Long? = null
)
data class TezosHeaderResponse(
@SerializedName("protocol")
var protocol: String? = null,
@SerializedName("hash")
var hash: String? = null
)

View file

@ -1,36 +0,0 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class TokenEmvTransferBody(
val CID: String,
val publicKey: String,
val amount: String,
val currency: String,
val recipient: String,
@SerializedName("fee_limit")
val feeLimit: String,
val sequence: Int,
val signature: String
)
data class TokenEmvTransferAnswer(
val error: String?,
val errorCode: Int?,
val success: Boolean?,
val tx_id: String?,
val blockchain_tx_id: String?
)
data class TokenEmvGetTransferFeeBody(
val CID: String,
val publicKey: String
)
data class TokenEmvGetTransferFeeAnswer(
val error: String?,
val errorCode: Int?,
val success: Boolean?,
val fee: String?,
val currency: String?
)

View file

@ -1,18 +0,0 @@
package com.tangem.di
import android.app.Application
import android.content.Context
import dagger.Module
import javax.inject.Singleton
import dagger.Provides
@Module
class AppModule {
@Provides
@Singleton
fun provideContext(application: Application): Context {
return application
}
}

View file

@ -1,23 +0,0 @@
package com.tangem.di
import com.tangem.ui.dialog.WaitSecurityDelayDialogNew
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@Module
internal class NavigatorModule {
@Singleton
@Provides
fun provideToastHelper(): ToastHelper {
return ToastHelper()
}
@Singleton
@Provides
fun provideWaitSecurityDelayDialogNew(): WaitSecurityDelayDialogNew {
return WaitSecurityDelayDialogNew()
}
}

View file

@ -1,55 +0,0 @@
package com.tangem.di
import com.tangem.data.network.Server
import dagger.Component
import retrofit2.Retrofit
import java.net.Socket
import javax.inject.Named
import javax.inject.Singleton
@Singleton
@Component(modules = [NetworkModule::class])
interface NetworkComponent {
@get:Named(Server.ApiInfura.URL_INFURA)
val retrofitInfura: Retrofit
@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
@get:Named(Server.ApiEstimatefee.URL_ESTIMATEFEE)
val retrofitEstimatefee: Retrofit
@get:Named(Server.ApiCoinmarket.URL_COINMARKET)
val retrofitCoinmarketcap: Retrofit
@get:Named(Server.ApiUpdateVersion.URL_UPDATE_VERSION)
val retrofitGitHubUserContent: Retrofit
@get:Named(Server.ApiRootstock.URL_ROOTSTOCK)
val retrofitRootstock: Retrofit
@get:Named(Server.ApiBlockcypher.URL_BLOCKCYPHER)
val retrofitBlockcypher: Retrofit
@get:Named(Server.ApiSoChain.URL)
val retrofitSoChain: Retrofit
@get:Named(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO)
val retrofitBlockchainInfo: Retrofit
@get:Named(Server.ApiDucatus.URL_DUCATUS)
val retrofitDucatus: Retrofit
@get:Named(Server.ApiBlockchair.URL_BLOCKCHAIR)
val retrofitBlockchair: Retrofit
@get:Named("socket")
val socket: Socket
}

View file

@ -1,216 +0,0 @@
package com.tangem.di
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import com.tangem.data.network.Server
import com.tangem.wallet.BuildConfig
import dagger.Module
import dagger.Provides
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.scalars.ScalarsConverterFactory
import java.io.IOException
import java.net.InetSocketAddress
import java.net.Socket
import java.net.SocketException
import javax.inject.Named
import javax.inject.Singleton
@Module
internal class NetworkModule {
@Singleton
@Provides
@Named(Server.ApiInfura.URL_INFURA)
fun provideRetrofitInfura(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiInfura.URL_INFURA)
.addConverterFactory(GsonConverterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiInfuraTestnet.URL_INFURA_TESTNET)
fun provideRetrofitInfuraTestnet(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiInfuraTestnet.URL_INFURA_TESTNET)
.addConverterFactory(GsonConverterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
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)
fun provideRetrofitRootstock(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiRootstock.URL_ROOTSTOCK)
.addConverterFactory(GsonConverterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiMaticTesnet.URL_MATIC_TESTNET)
fun provideRetrofitMaticTestnet(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiMaticTesnet.URL_MATIC_TESTNET)
.addConverterFactory(GsonConverterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiEstimatefee.URL_ESTIMATEFEE)
fun provideRetrofitEstimatefee(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiEstimatefee.URL_ESTIMATEFEE)
.addConverterFactory(GsonConverterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiUpdateVersion.URL_UPDATE_VERSION)
fun provideGithubusercontent(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiUpdateVersion.URL_UPDATE_VERSION)
.addConverterFactory(GsonConverterFactory.create())
.client(createOkHttpClient())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiCoinmarket.URL_COINMARKET)
fun provideRetrofitCoinmarketcap(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiCoinmarket.URL_COINMARKET)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// if (BuildConfig.DEBUG)
// builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiBlockcypher.URL_BLOCKCYPHER)
fun provideRetrofitBlockcypher(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiBlockcypher.URL_BLOCKCYPHER)
.addConverterFactory(GsonConverterFactory.create())
.client(createOkHttpClient())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiSoChain.URL)
fun provideRetrofitSoChain(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiSoChain.URL)
.addConverterFactory(GsonConverterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO)
fun provideRetrofitBlockchainInfo(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiDucatus.URL_DUCATUS)
fun provideRetrofitDucatus(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiDucatus.URL_DUCATUS)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiBlockchair.URL_BLOCKCHAIR)
fun provideRetrofitBlockchair(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiBlockchair.URL_BLOCKCHAIR)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
private fun createOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(createHttpLoggingInterceptor()).build()
}
private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor {
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
return logging
}
@Provides
@Named("socket")
fun provideSocket(): Socket {
val socket = Socket()
try {
socket.soTimeout = 2000
try {
socket.bind(InetSocketAddress(0))
} catch (e: IOException) {
e.printStackTrace()
}
} catch (e: SocketException) {
e.printStackTrace()
}
return socket
}
}

View file

@ -1,70 +0,0 @@
package com.tangem.di
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.view.ViewGroup
import android.widget.Toast
import com.google.android.material.snackbar.Snackbar
import com.tangem.Constant
import com.tangem.wallet.R
import java.util.*
class ToastHelper {
fun showSnackbarUpdateVersion(context: Context, vg: ViewGroup, versionName: String) {
Snackbar.make(vg, String.format(context.getString(R.string.main_screen_new_version_toast), versionName), Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.main_screen_btn_update) {
try {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(Constant.URL_TANGEM)
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
e.printStackTrace()
}
}.show()
}
fun showSnackbarSuccess(context: Context, vg: ViewGroup, message: String) {
val snackbar = Snackbar.make(vg, message, Snackbar.LENGTH_INDEFINITE)
val snackView = snackbar.view
snackView.setBackgroundColor(context.resources.getColor(R.color.msg_okay))
snackbar.setAction(R.string.general_ok) {
snackbar.dismiss()
}
snackbar.show()
}
fun showSnackbarError(context: Context, vg: ViewGroup, message: String) {
val snackbar = Snackbar.make(vg, message, Snackbar.LENGTH_INDEFINITE)
val snackView = snackbar.view
snackView.setBackgroundColor(context.resources.getColor(R.color.msg_err))
snackbar.setAction(R.string.general_ok) {
snackbar.dismiss()
}
snackbar.show()
}
fun showSnackbarWarning(context: Context, vg: ViewGroup, message: String) {
val snackbar = Snackbar.make(vg, message, Snackbar.LENGTH_LONG)
val snackView = snackbar.view
snackView.setBackgroundColor(context.resources.getColor(R.color.msg_err))
snackbar.show()
}
private var singleToast: Toast? = null
private var showTime: Date = Date()
fun showSingleToast(context: Context?, text: String) {
if (singleToast == null || !singleToast!!.view.isShown || showTime.time + 2000 < Date().time) {
if (singleToast != null)
singleToast!!.cancel()
if (context != null) {
singleToast = Toast.makeText(context, text, Toast.LENGTH_LONG)
singleToast!!.show()
showTime = Date()
}
}
}
}

View file

@ -1,13 +0,0 @@
package com.tangem.di
import com.tangem.ui.activity.MainActivity
import dagger.Component
import javax.inject.Singleton
@Singleton
@Component(modules = [NavigatorModule::class])
interface ToastHelperComponent {
fun inject(activity: MainActivity)
}

View file

@ -0,0 +1,70 @@
package com.tangem.tap
import android.content.pm.ActivityInfo
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.tangem.CardFilter
import com.tangem.Config
import com.tangem.TangemSdk
import com.tangem.common.extensions.CardType
import com.tangem.tangem_sdk_new.extensions.init
import com.tangem.tap.common.redux.NotificationsHandler
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.TangemSdkManager
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import java.lang.ref.WeakReference
import java.util.*
import kotlin.coroutines.CoroutineContext
lateinit var tangemSdk: TangemSdk
lateinit var tangemSdkManager: TangemSdkManager
var notificationsHandler: NotificationsHandler? = null
private val coroutineContext: CoroutineContext
get() = Job() + Dispatchers.IO + initCoroutineExceptionHandler()
val scope = CoroutineScope(coroutineContext)
private fun initCoroutineExceptionHandler(): CoroutineExceptionHandler {
return CoroutineExceptionHandler { _, throwable -> throw throwable }
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
store.dispatch(NavigationAction.ActivityCreated(WeakReference(this)))
tangemSdk = TangemSdk.init(
this, Config(cardFilter = CardFilter(EnumSet.allOf(CardType::class.java)))
)
tangemSdkManager = TangemSdkManager(this)
}
override fun onResume() {
super.onResume()
notificationsHandler = NotificationsHandler(fragment_container)
if (supportFragmentManager.backStackEntryCount == 0) {
store.dispatch(
NavigationAction.NavigateTo(AppScreen.Home)
)
}
}
override fun onStop() {
notificationsHandler = null
super.onStop()
}
override fun onDestroy() {
store.dispatch(NavigationAction.ActivityDestroyed)
super.onDestroy()
}
}

View file

@ -0,0 +1,14 @@
package com.tangem.tap
import android.app.Application
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.appReducer
import org.rekotlin.Store
val store = Store(
reducer = ::appReducer,
middleware = AppState.getMiddleware(),
state = AppState()
)
class TapApplication : Application()

View file

@ -0,0 +1,3 @@
package com.tangem.tap.common.entities
abstract class Button(val enabled: Boolean)

View file

@ -0,0 +1,15 @@
package com.tangem.tap.common.extensions
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import java.io.ByteArrayOutputStream
fun Bitmap.toByteArray(): ByteArray {
val stream = ByteArrayOutputStream()
this.compress(Bitmap.CompressFormat.JPEG, 20, stream)
return stream.toByteArray()
}
fun ByteArray.toBitmap(): Bitmap {
return BitmapFactory.decodeByteArray(this, 0, this.size)
}

View file

@ -0,0 +1,38 @@
package com.tangem.tap.common.extensions
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.features.home.HomeFragment
import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.wallet.R
fun FragmentActivity.openFragment(screen: AppScreen, addToBackStack: Boolean = true) {
val transaction = this.supportFragmentManager.beginTransaction()
.replace(
R.id.fragment_container,
fragmentFactory(screen),
screen.name
)
if (addToBackStack && screen != AppScreen.Home) transaction.addToBackStack(null)
transaction.commit();
}
fun FragmentActivity.popBackTo(screen: AppScreen?, inclusive: Boolean = false) {
val inclusiveFlag = if (inclusive) FragmentManager.POP_BACK_STACK_INCLUSIVE else 0
this.supportFragmentManager.popBackStack(screen?.name, inclusiveFlag)
}
fun FragmentActivity.getPreviousScreen(): AppScreen? {
val indexOfLastFragment = this.supportFragmentManager.backStackEntryCount - 1
val tag = this.supportFragmentManager.getBackStackEntryAt(indexOfLastFragment).name
return tag?.let { AppScreen.valueOf(tag) }
}
private fun fragmentFactory(screen: AppScreen): Fragment {
return when (screen) {
AppScreen.Home -> HomeFragment()
AppScreen.Wallet -> WalletFragment()
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.tap.common.extensions
import android.graphics.Bitmap
import android.graphics.Color
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import java.util.*
fun String.toQrCode(): Bitmap {
val hintMap = Hashtable<EncodeHintType, Any>()
hintMap[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.M // H = 30% damage
hintMap[EncodeHintType.MARGIN] = 2
val qrCodeWriter = QRCodeWriter()
val size = 256
val bitMatrix = qrCodeWriter.encode(this, BarcodeFormat.QR_CODE, size, size, hintMap)
val width = bitMatrix.width
val bmp = Bitmap.createBitmap(width, width, Bitmap.Config.RGB_565)
for (x in 0 until width) {
for (y in 0 until width) {
bmp.setPixel(y, x, if (bitMatrix.get(x, y)) Color.BLACK else Color.WHITE)
}
}
return bmp
}

View file

@ -0,0 +1,127 @@
package com.tangem.tap.common.extensions
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Build
import android.text.Spannable
import android.text.style.ForegroundColorSpan
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.text.toSpannable
import androidx.fragment.app.Fragment
import com.google.android.material.card.MaterialCardView
fun Fragment.getDrawable(@DrawableRes drawableResId: Int): Drawable? {
return ContextCompat.getDrawable(requireContext(), drawableResId)
}
fun View.show(show: Boolean) {
if (show) this.visibility = View.VISIBLE else this.visibility = View.GONE
}
fun View.show() {
this.visibility = View.VISIBLE
}
fun View.hide() {
this.visibility = View.GONE
}
fun View.makeInvisible() {
this.visibility = View.INVISIBLE
}
fun Context.dpToPixels(dp: Int): Int =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), this.resources.displayMetrics
).toInt()
fun MaterialCardView.setMargins(
marginLeftDp: Int = 16,
marginTopDp: Int = 8,
marginRightDp: Int = 16,
marginBottomDp: Int = 8
) {
val params = this.layoutParams
(params as ViewGroup.MarginLayoutParams).setMargins(
context.dpToPixels(marginLeftDp),
context.dpToPixels(marginTopDp),
context.dpToPixels(marginRightDp),
context.dpToPixels(marginBottomDp)
)
this.layoutParams = params
}
fun Activity.setSystemBarTextColor(setTextDark: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val flags = this.window.decorView.systemUiVisibility
// Update the SystemUiVisibility dependening on whether we want a Light or Dark theme.
this.window.decorView.systemUiVisibility =
if (setTextDark) {
flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
} else {
flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
}
}
fun String.colorSegment(
context: Context,
color: Int,
startIndex: Int = 0,
endIndex: Int = this.length
): Spannable {
return this.toSpannable()
.also { spannable ->
spannable.setSpan(
ForegroundColorSpan(ContextCompat.getColor(context, color)),
startIndex,
endIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
fun View.hideKeyboard() {
val inputMethodManager = context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE) as? InputMethodManager
inputMethodManager?.hideSoftInputFromWindow(this.windowToken, 0)
}
fun Context.copyToClipboard(value: Any, label: String = "") {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return
val clip: ClipData = ClipData.newPlainText(label, value.toString())
clipboard.setPrimaryClip(clip)
}
fun Context.shareText(text: String) {
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, text)
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
startActivity(shareIntent)
}
fun Fragment.shareText(text: String) {
requireContext().shareText(text)
}
fun ViewGroup.inflate(viewToInflate: Int, rootView: ViewGroup?, parent: ViewGroup) {
if (rootView == null) {
val inflatedView = LayoutInflater.from(context).inflate(viewToInflate, rootView)
parent.addView(inflatedView)
}
}

View file

@ -0,0 +1,19 @@
package com.tangem.tap.common.redux
import com.tangem.tap.common.redux.navigation.navigationReducer
import com.tangem.tap.features.wallet.redux.walletReducer
import org.rekotlin.Action
fun appReducer(action: Action, state: AppState?): AppState {
requireNotNull(state)
if (action is AppAction.RestoreState) return action.state
return AppState(
navigationState = navigationReducer(action, state),
// homeState = homeReducer(action, state),
walletState = walletReducer(action, state),
)
}
sealed class AppAction : Action {
data class RestoreState(val state: AppState) : AppAction()
}

View file

@ -0,0 +1,25 @@
package com.tangem.tap.common.redux
import com.tangem.tap.common.redux.navigation.NavigationState
import com.tangem.tap.common.redux.navigation.navigationMiddleware
import com.tangem.tap.features.home.redux.homeMiddleware
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.walletMiddleware
import org.rekotlin.Middleware
import org.rekotlin.StateType
data class AppState(
val navigationState: NavigationState = NavigationState(),
val walletState: WalletState = WalletState()
) : StateType {
companion object {
fun getMiddleware(): List<Middleware<AppState>> {
return listOf(
navigationMiddleware, notificationsMiddleware,
homeMiddleware, walletMiddleware,
)
}
}
}

View file

@ -0,0 +1,48 @@
package com.tangem.tap.common.redux
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.snackbar.Snackbar
import com.tangem.TangemError
import com.tangem.tap.notificationsHandler
import org.rekotlin.Action
import org.rekotlin.Middleware
import java.lang.ref.WeakReference
class NotificationsHandler(coordinatorLayout: CoordinatorLayout) {
private val coordinatorLayoutWeak = WeakReference(coordinatorLayout)
fun showNotification(message: String) {
coordinatorLayoutWeak.get()?.let { layout ->
Snackbar.make(layout, message, Snackbar.LENGTH_LONG)
.also { snackbar -> snackbar.show() }
}
}
fun showNotification(message: Int) {
coordinatorLayoutWeak.get()?.let {
showNotification(it.context.getString(message))
}
}
}
val notificationsMiddleware: Middleware<AppState> = { dispatch, state ->
{ next ->
{ action ->
if (action is NotificationAction) {
notificationsHandler?.showNotification(action.messageResource)
}
if (action is ErrorAction) {
notificationsHandler?.showNotification(action.error.customMessage)
}
next(action)
}
}
}
interface NotificationAction : Action {
val messageResource: Int
}
interface ErrorAction : Action {
val error: TangemError
}

View file

@ -0,0 +1,7 @@
package com.tangem.tap.common.redux
import org.rekotlin.Action
abstract class Request : Action {
abstract suspend fun execute()
}

View file

@ -0,0 +1,15 @@
package com.tangem.tap.common.redux.navigation
import androidx.fragment.app.FragmentActivity
import org.rekotlin.Action
import java.lang.ref.WeakReference
sealed class NavigationAction : Action {
data class NavigateTo(val screen: AppScreen, val addToBackstack: Boolean = true) :
NavigationAction()
data class PopBackTo(val screen: AppScreen? = null) : NavigationAction()
data class ActivityCreated(val activity: WeakReference<FragmentActivity>) : NavigationAction()
object ActivityDestroyed : NavigationAction()
}

View file

@ -0,0 +1,30 @@
package com.tangem.tap.common.redux.navigation
import com.tangem.tap.common.extensions.openFragment
import com.tangem.tap.common.extensions.popBackTo
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.store
import org.rekotlin.Middleware
val navigationMiddleware: Middleware<AppState> = { dispatch, state ->
{ next ->
{ action ->
if (action is NavigationAction) {
val navState = store.state.navigationState
when (action) {
is NavigationAction.NavigateTo -> {
navState.activity?.get()?.openFragment(action.screen, action.addToBackstack)
}
is NavigationAction.PopBackTo -> {
if (action.screen == AppScreen.Home) {
navState.activity?.get()?.popBackTo(null, true)
} else {
navState.activity?.get()?.popBackTo(action.screen)
}
}
}
}
next(action)
}
}
}

View file

@ -0,0 +1,25 @@
package com.tangem.tap.common.redux.navigation
import com.tangem.tap.common.extensions.getPreviousScreen
import com.tangem.tap.common.redux.AppState
import org.rekotlin.Action
fun navigationReducer(action: Action, state: AppState): NavigationState {
val navigationAction = action as? NavigationAction ?: return state.navigationState
val navState = state.navigationState
return when (navigationAction) {
is NavigationAction.NavigateTo -> {
navState.copy(backStack = navState.backStack + navigationAction.screen)
}
is NavigationAction.PopBackTo -> {
val screen =
navigationAction.screen ?: navState.activity?.get()?.getPreviousScreen()
val index = navState.backStack.lastIndexOf(screen) + 1
state.navigationState.copy(backStack = navState.backStack.subList(0, index))
}
is NavigationAction.ActivityCreated -> navState.copy(activity = navigationAction.activity)
is NavigationAction.ActivityDestroyed -> navState.copy(activity = null)
}
}

View file

@ -0,0 +1,12 @@
package com.tangem.tap.common.redux.navigation
import androidx.fragment.app.FragmentActivity
import org.rekotlin.StateType
import java.lang.ref.WeakReference
data class NavigationState(
val backStack: List<AppScreen> = listOf(AppScreen.Home),
val activity: WeakReference<FragmentActivity>? = null
) : StateType
enum class AppScreen { Home, Wallet }

Some files were not shown because too many files have changed in this diff Show more