Updated on 2026-08-14

This commit is contained in:
Tangem 2018-09-05 01:06:51 +03:00
parent 22d155e526
commit b1134eb19e
17 changed files with 3029 additions and 7 deletions

View file

@ -0,0 +1,327 @@
package com.tangem.data.network;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
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.util.Util;
import com.tangem.wallet.R;
import java.io.IOException;
import java.util.Arrays;
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.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
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.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
/**
* 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

@ -0,0 +1,283 @@
package com.tangem.data.network;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
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.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 okhttp3.logging.HttpLoggingInterceptor;
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;
/**
* HTTP
* Used in Cryptonit_OtherAPI service
*/
public class Cryptonit_OtherAPI {
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 Cryptonit_OtherAPI(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

@ -0,0 +1,358 @@
package com.tangem.data.network;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.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 okhttp3.logging.HttpLoggingInterceptor;
import okio.Buffer;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
/**
* 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);
}
}
}