Updated on 2026-08-14

This commit is contained in:
Tangem 2019-01-22 14:29:46 +03:00
commit 18f2a40f24
30 changed files with 2059 additions and 121 deletions

View file

@ -13,7 +13,9 @@ public enum Blockchain {
EthereumTestNet("ETH/test", "ETH", 1.0, R.drawable.ic_logo_ethereum_testnet, "Ethereum Testnet"),
Token("Token", "ERC20", 1.0, R.drawable.ic_logo_bat_token, "Ethereum"),
BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.ic_logo_bitcoin_cash, "Bitcoin Cash"),
Litecoin("LTC", "LTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Litecoin");
Litecoin("LTC", "LTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Litecoin"),
Rootstock("RSK", "RBTC", 1.0, R.drawable.ic_logo_bitcoin, "Rootstock"),
RootstockToken("Token", "ERC20", 1.0, R.drawable.ic_logo_bat_token, "Rootstock");
Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) {
mID = ID;

View file

@ -0,0 +1,32 @@
package com.tangem.data.network;
import com.tangem.data.network.model.InsightBody;
import com.tangem.data.network.model.InsightResponse;
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<InsightResponse>> insightUnspent(@Path("address") String address);
@GET(ServerApiInsight.INSIGHT_TRANSACTION)
Call<InsightResponse> insightTransaction(@Path("transaction") String transaction);
@GET(ServerApiInsight.INSIGHT_FEE)
Call<InsightResponse> insightFee();
@Headers("Content-Type: application/json")
@POST(ServerApiInsight.INSIGHT_SEND)
Call<InsightResponse> insightSend(@Body InsightBody body );
}

View file

@ -0,0 +1,15 @@
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

@ -28,7 +28,15 @@ public class Server {
public static final String URL_INFURA = ServerURL.API_INFURA;
public static class Method {
static final String MAIN = URL_INFURA + "613a0b14833145968b1f656240c7d245";
static final String MAIN = URL_INFURA + "v3/613a0b14833145968b1f656240c7d245";
}
}
public static class ApiRootstock {
public static final String URL_ROOTSTOCK = ServerURL.API_ROOTSTOCK;
public static class Method {
static final String MAIN = URL_ROOTSTOCK;
}
}
@ -44,5 +52,4 @@ public class Server {
static final String N_6 = URL_ESTIMATEFEE + "n/6";
}
}
}

View file

@ -0,0 +1,131 @@
package com.tangem.data.network;
import android.support.annotation.NonNull;
import android.util.Log;
import com.tangem.data.network.model.InsightBody;
import com.tangem.data.network.model.InsightResponse;
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/{transaction}";
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 InsightBodyListener insightBodyListener;
public interface InsightBodyListener {
void onSuccess(String method, InsightResponse insightResponse);
void onSuccess(String method, List<InsightResponse> utxoList);
void onFail(String method, String message);
}
public void setInsightResponse(InsightBodyListener listener) {
insightBodyListener = listener;
}
public void insight(String method, String wallet, String tx) {
requestsCount++;
String insightURL = "http://130.185.109.17:3001/insight-api"; //TODO: make random selection
this.lastNode = insightURL; //TODO: show node instead of URL
Retrofit retrofitInsight = new Retrofit.Builder() //TODO: move to NetworkModule+NetworkComponent if possible
.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<InsightResponse>> call = insightApi.insightUnspent(wallet);
call.enqueue(new Callback<List<InsightResponse>>() {
@Override
public void onResponse(@NonNull Call<List<InsightResponse>> call, @NonNull Response<List<InsightResponse>> response) {
if (response.code() == 200) {
requestsCount--;
insightBodyListener.onSuccess(method, response.body());
Log.i(TAG, "insight " + method + " onResponse " + response.code());
} else {
insightBodyListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "insight " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<List<InsightResponse>> call, @NonNull Throwable t) {
insightBodyListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "insight " + method + " onFailure " + t.getMessage());
}
});
} else {
Call<InsightResponse> call = null;
switch (method) {
case INSIGHT_ADDRESS:
call = insightApi.insightAddress(wallet);
break;
case INSIGHT_UNSPENT_OUTPUTS:
break;
case INSIGHT_TRANSACTION:
call = insightApi.insightTransaction(tx);
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) {
if (response.code() == 200) {
requestsCount--;
insightBodyListener.onSuccess(method, response.body());
Log.i(TAG, "insight " + method + " onResponse " + response.code());
} else {
insightBodyListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "insight " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InsightResponse> call, @NonNull Throwable t) {
insightBodyListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "insight " + method + " onFailure " + t.getMessage());
}
});
}
}
}

View file

@ -0,0 +1,105 @@
package com.tangem.data.network;
import android.support.annotation.NonNull;
import android.util.Log;
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
* Infura
* <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 RootstockBodyListener rootstockBodyListener;
public interface RootstockBodyListener {
void onSuccess(String method, InfuraResponse infuraResponse);
void onFail(String method, String message);
}
public void setRootstockResponse(RootstockBodyListener listener) {
rootstockBodyListener = listener;
}
public void rootstock(String method, int id, String wallet, String contract, String tx) {
requestsCount++;
RootstockApi rootstockApi = App.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) {
if (response.code() == 200) {
requestsCount--;
rootstockBodyListener.onSuccess(method, response.body());
Log.i(TAG, "rootstock " + method + " onResponse " + response.code());
} else {
rootstockBodyListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "rootstock " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
rootstockBodyListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "rootstock " + method + " onFailure " + t.getMessage());
}
});
}
}

View file

@ -3,7 +3,8 @@ package com.tangem.data.network;
class ServerURL {
static final String API_TANGEM = "https://verify.tangem.com/";
static final String API_COINMARKETCAP = "https://api.coinmarketcap.com/";
static final String API_INFURA = "https://mainnet.infura.io/v3/";
static final String API_INFURA = "https://mainnet.infura.io/";
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/";
}

View file

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

View file

@ -0,0 +1,38 @@
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("txid")
var txid: String = "",
@SerializedName("satoshis")
var satoshis: Long? = null,
@SerializedName("height")
var height: Int? = null,
@SerializedName("2")
var fee2: String = "",
@SerializedName("3")
var fee3: String = "",
@SerializedName("6")
var fee6: String = "",
@SerializedName("rawtx")
var rawtx: String = "",
@SerializedName("error")
var error: String = ""
)