Deterministic Address Generation

-Added deterministic address generation
-Added testnet connections

-Need to cleanup
This commit is contained in:
Toby Woerthle 2018-01-11 01:17:15 -05:00
parent f03c594830
commit 0b2253b8f7
10 changed files with 460 additions and 118 deletions

View File

@ -15,8 +15,11 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.flashwifi.wifip2p.iotaAPI.Requests.WalletAddressAndBalanceChecker;
import net.glxn.qrgen.android.QRCode;
import java.util.Iterator;
import java.util.List;
/**
@ -28,14 +31,14 @@ public class FundWalletFragment extends Fragment {
private static final int TASK_COMPLETE = 1;
private String seed;
private String address;
private String depositAddress;
private String balance;
TextView balanceTextView = null;
TextView addressTextView = null;
ImageView qrImageView = null;
private TextView balanceTextView;
private TextView addressTextView;
private ImageView qrImageView;
Handler mHandler;
private Handler mHandler;
public FundWalletFragment() {
// Required empty public constructor
@ -70,22 +73,18 @@ public class FundWalletFragment extends Fragment {
if(returnStatus == "noError"){
balanceTextView.setText(balance + " i");
addressTextView.setText(address);
createAddressQRCode(address);
Toast.makeText(getActivity(), "Balance and address updated",
Toast.LENGTH_SHORT).show();
addressTextView.setText(depositAddress);
createAddressQRCode(depositAddress);
makeToastFundWalletFragment("Balance and address updated");
}
else if (returnStatus == "addressError"){
Toast.makeText(getActivity(), "Error getting address",
Toast.LENGTH_SHORT).show();
makeToastFundWalletFragment("Error getting address");
}
else if (returnStatus == "balanceError"){
Toast.makeText(getActivity(), "Error getting balance",
Toast.LENGTH_SHORT).show();
makeToastFundWalletFragment("Error getting balance");
}
else{
Toast.makeText(getActivity(), "Unknown error",
Toast.LENGTH_SHORT).show();
makeToastFundWalletFragment("Unknown error");
}
break;
}
@ -93,6 +92,12 @@ public class FundWalletFragment extends Fragment {
};
}
private void makeToastFundWalletFragment(String s) {
if(getActivity() != null){
Toast.makeText(getActivity(), s, Toast.LENGTH_SHORT).show();
}
}
private void createAddressQRCode(String address) {
Bitmap myBitmap = QRCode.from(address).bitmap();
qrImageView.setImageBitmap(myBitmap);
@ -129,12 +134,17 @@ public class FundWalletFragment extends Fragment {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
WalletAddressAndBalanceChecker addressAndBalanceCheckerbalanceChecker = new WalletAddressAndBalanceChecker();
List<String> addressList = addressAndBalanceCheckerbalanceChecker.getAddress(seed);
WalletAddressAndBalanceChecker addressAndBalanceChecker = new WalletAddressAndBalanceChecker();
List<String> addressList = addressAndBalanceChecker.getAddress(seed);
if(addressList != null){
address = addressList.get(0);
balance = addressAndBalanceCheckerbalanceChecker.getBalance(addressList);
System.out.println("|AddressListReturned:|");
System.out.println(addressList.size());
System.out.println(addressList.get(addressList.size()-1));
depositAddress = addressList.get(addressList.size()-1);
balance = addressAndBalanceChecker.getBalance(addressList);
if(balance != null){
Message completeMessage = mHandler.obtainMessage(TASK_COMPLETE, "noError");
completeMessage.sendToTarget();

View File

@ -88,7 +88,10 @@ public class HomeActivity extends AppCompatActivity {
private void generateNewSeed() {
// generate the seed
final String seed = SeedRandomGenerator.generateNewSeed();
//final String seed = SeedRandomGenerator.generateNewSeed();
//Set Testnet seed here:
final String seed = "EJ9SPL9GIK9EFICFRPQU9LLSCPNESAWRPYVKQRBZQVACRBVKVZRIWOWUBRJKWJMXLPAXDXWI9IDMAOTOZ";
TextView seedText = (TextView) findViewById(R.id.seedTextView);
seedText.setVisibility(View.VISIBLE);

View File

@ -3,6 +3,7 @@ package com.flashwifi.wifip2p;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Intent;
import android.hardware.Camera;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;

View File

@ -28,20 +28,8 @@ public class QRScannerFragment extends Fragment implements ZXingScannerView.Resu
ViewGroup contentFrame = (ViewGroup) getActivity().findViewById(R.id.content_frame);
contentFrame.addView(scannerView);
//getActivity().setContentView(scannerView);
}
/*
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
scannerView = new ZXingScannerView(getActivity());
temp = getActivity().findViewById(android.R.id.content);
getActivity().setContentView(scannerView);
return scannerView;
}
*/
@Override
public void onResume() {
super.onResume();
@ -53,10 +41,12 @@ public class QRScannerFragment extends Fragment implements ZXingScannerView.Resu
public void onPause() {
super.onPause();
scannerView.stopCamera();
scannerView.stopCameraPreview();
}
@Override
public void handleResult(Result result) {
String resultText = result.getText();
//Edit via reference (shallow copy)

View File

@ -1,56 +0,0 @@
package com.flashwifi.wifip2p;
/**
* Created by Toby on 1/6/2018.
*/
import java.util.List;
import jota.IotaAPI;
import jota.dto.response.GetBalancesResponse;
import jota.dto.response.GetNewAddressResponse;
import jota.error.ArgumentException;
public class WalletAddressAndBalanceChecker {
private static IotaAPI api;
//GetNodeInfoResponse response = api.getNodeInfo();
public WalletAddressAndBalanceChecker() {
//Local node:
//api = new IotaAPI.Builder().build();
//Local node:
api = new IotaAPI.Builder()
.protocol("http")
.host("node.iotawallet.info")
.port("14265")
.build();
}
public String getBalance(List<String> inAaddresses){
try {
GetBalancesResponse balanceResultResponse = api.getBalances(100, inAaddresses);
String balance = balanceResultResponse.getBalances()[0];
return balance;
} catch (ArgumentException e) {
e.printStackTrace();
System.out.println("getBalance Error!");
}
return null;
}
public List<String> getAddress(String seed) {
List<String> addressList = null;
GetNewAddressResponse addressResponse = null;
try {
addressResponse = api.getNewAddress(seed, 2, 0, false, 1, true);
} catch (ArgumentException e) {
e.printStackTrace();
}
if(addressResponse != null){
addressList = addressResponse.getAddresses();
}
return addressList;
}
}

View File

@ -1,33 +1,56 @@
package com.flashwifi.wifip2p;
import android.Manifest;
import android.app.FragmentTransaction;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Fragment;
import android.os.Parcelable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import com.flashwifi.wifip2p.iotaAPI.Requests.WalletAddressAndBalanceChecker;
import com.flashwifi.wifip2p.iotaAPI.Requests.WalletTransferRequest;
import java.util.List;
import jota.IotaAPI;
import jota.error.ArgumentException;
import jota.utils.Checksum;
import static android.Manifest.permission.CAMERA;
/**
* A simple {@link Fragment} subclass.
* Use the {@link WithdrawWalletFragment#newInstance} factory method to
* create an instance of this fragment.
* Sources: https://github.com/iotaledger/android-wallet-app
*/
public class WithdrawWalletFragment extends Fragment {
private String seed;
ImageButton qrScannerButton;
EditText editTextAddress;
private static final int BALANCE_RETRIEVE_TASK_COMPLETE = 1;
private static final int TRANSFER_TASK_COMPLETE = 2;
private String appWalletSeed;
private String appWalletBalance;
private ImageButton qrScannerButton;
private EditText editTextAddress;
private EditText editTextAmount;
private EditText editTextMessage;
private EditText editTextTag;
private Button sendButton;
private TextView balanceTextView;
private Handler mHandler;
public WithdrawWalletFragment() {
// Required empty public constructor
@ -50,8 +73,39 @@ public class WithdrawWalletFragment extends Fragment {
super.onCreate(savedInstanceState);
Bundle bundle = this.getArguments();
if (bundle != null) {
seed = bundle.getString("seed");
appWalletSeed = bundle.getString("seed");
}
//Handle post-asynctask activities of updating UI
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message inputMessage) {
switch (inputMessage.what) {
case BALANCE_RETRIEVE_TASK_COMPLETE:
String returnStatus = (String) inputMessage.obj;
if(returnStatus == "noError"){
balanceTextView.setText(appWalletBalance + " i");
makeToastFundWalletFragment("Balance updated");
}
else if (returnStatus == "addressError"){
makeToastFundWalletFragment("Error getting address");
}
else if (returnStatus == "balanceError"){
makeToastFundWalletFragment("Error getting balance");
}
else{
makeToastFundWalletFragment("Unknown error");
}
break;
case TRANSFER_TASK_COMPLETE:
String transferStatus = (String) inputMessage.obj;
makeToastFundWalletFragment(transferStatus);
}
}
};
}
@Override
@ -59,20 +113,16 @@ public class WithdrawWalletFragment extends Fragment {
Bundle savedInstanceState) {
// Inflate the layout for this fragment
if(savedInstanceState != null){
QRCodeResult qrResult = savedInstanceState.getParcelable("result");
if(qrResult != null){
String address = qrResult.getAddress();
Toast.makeText(getActivity(), "Address: "+address, Toast.LENGTH_SHORT).show();
}
}
Toast.makeText(getActivity(), "Make View", Toast.LENGTH_SHORT).show();
View withdrawWalletFragmentView = inflater.inflate(R.layout.fragment_withdraw_wallet, container, false);
sendButton = (Button) withdrawWalletFragmentView.findViewById(R.id.WithdrawWalletSend);
qrScannerButton = (ImageButton) withdrawWalletFragmentView.findViewById(R.id.WithdrawWalletQRScannerButton);
editTextAddress = (EditText) withdrawWalletFragmentView.findViewById(R.id.editTextAddress);
editTextAddress = (EditText) withdrawWalletFragmentView.findViewById(R.id.WithdrawWalletAddress);
editTextAmount = (EditText) withdrawWalletFragmentView.findViewById(R.id.WithdrawWalletAmount);
editTextMessage = (EditText) withdrawWalletFragmentView.findViewById(R.id.WithdrawWalletMessage);
editTextTag = (EditText) withdrawWalletFragmentView.findViewById(R.id.WithdrawWalletTag);
balanceTextView = (TextView) withdrawWalletFragmentView.findViewById(R.id.WithdrawWalletBalanceValue);
// Set Listeners
qrScannerButton.setOnClickListener(new View.OnClickListener() {
@ -82,9 +132,58 @@ public class WithdrawWalletFragment extends Fragment {
}
});
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendButtonClick();
}
});
Toast.makeText(getActivity(), "Retrieving balance...",
Toast.LENGTH_SHORT).show();
AsyncTask.execute(new Runnable() {
@Override
public void run() {
WalletAddressAndBalanceChecker addressAndBalanceChecker = new WalletAddressAndBalanceChecker();
List<String> addressList = addressAndBalanceChecker.getAddress(appWalletSeed);
System.out.println("==Start==");
for (String s : addressList) {
System.out.println("Address");
System.out.println(s);
}
if(addressList != null){
appWalletBalance = addressAndBalanceChecker.getBalance(addressList);
if(appWalletBalance != null){
Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE, "noError");
completeMessage.sendToTarget();
}
else{
//Balance Retrieval Error
Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE, "balanceError");
completeMessage.sendToTarget();
}
}
else{
//Address Retrieval Error
Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE, "addressError");
completeMessage.sendToTarget();
}
}
});
return withdrawWalletFragmentView;
}
private void makeToastFundWalletFragment(String s) {
if(getActivity() != null){
Toast.makeText(getActivity(), s, Toast.LENGTH_SHORT).show();
}
}
private void qrScannerButtonClick() {
if (!hasCameraPermission(getActivity())) {
Toast.makeText(getActivity(), "Camera permission not granted", Toast.LENGTH_SHORT).show();
@ -94,12 +193,68 @@ public class WithdrawWalletFragment extends Fragment {
}
}
private void sendButtonClick() {
final String sendAddress = editTextAddress.getText().toString();
final String sendAmount = editTextAmount.getText().toString();
final String message = editTextMessage.getText().toString();
final String tag = editTextTag.getText().toString();
if(appWalletBalance == null || appWalletBalance.isEmpty()){
Toast.makeText(getActivity(), "Please wait for balance to be retrieved", Toast.LENGTH_SHORT).show();
}
else if(isValidAddress() == false){
Toast.makeText(getActivity(), "Please enter a valid recipient address", Toast.LENGTH_SHORT).show();
}
else if (sendAmount.isEmpty() == false && (Long.parseLong(appWalletBalance) < Long.parseLong(editTextAmount.getText().toString()))) {
Toast.makeText(getActivity(), "Not enough credit in wallet", Toast.LENGTH_SHORT).show();
}
else{
if (sendAmount.isEmpty() || sendAmount.equals("0")) {
Toast.makeText(getActivity(), "0 send amount will be used", Toast.LENGTH_SHORT).show();
}
AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
.setMessage("Are you sure you want to send the transfer?")
.setCancelable(false)
.setPositiveButton("OK",null)
.setNegativeButton("Cancel",null)
.create();
alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//Send transfer
AsyncTask.execute(new Runnable() {
@Override
public void run() {
if(sendAmount.isEmpty()){
String zeroSendAmount = "0";
WalletTransferRequest transferRequest = new WalletTransferRequest(sendAddress,appWalletSeed,zeroSendAmount,message,tag,getActivity());
String result = transferRequest.sendRequest();
}
else{
WalletTransferRequest transferRequest = new WalletTransferRequest(sendAddress,appWalletSeed,sendAmount,message,tag,getActivity());
String result = transferRequest.sendRequest();
Message completeMessage = mHandler.obtainMessage(TRANSFER_TASK_COMPLETE, result);
completeMessage.sendToTarget();
}
}
});
//TODO: Empty all fields
}
});
alertDialog.show();
}
}
public static boolean hasCameraPermission(Context context) {
int result = context.checkCallingOrSelfPermission(CAMERA);
return result == PackageManager.PERMISSION_GRANTED;
}
private void launchQRScanner() {
/*
TODO:Uncomment to re-enable scanner
Fragment fragment = new QRScannerFragment();
Bundle bundle = new Bundle();
@ -116,6 +271,10 @@ public class WithdrawWalletFragment extends Fragment {
.addToBackStack(null)
.commit();
//fragment.setRetainInstance(true);
*/
editTextAddress.setText("ULRSUDTQLEQLXUMXEOWPEUIHRZUJFPUZRHVBZC9XYIVZJWGFOJNLDHQNQAZPPVOSTVBUT9T9EJRNMGGO9");
}
@Override
@ -132,4 +291,16 @@ public class WithdrawWalletFragment extends Fragment {
}
}
private boolean isValidAddress() {
String address = editTextAddress.getText().toString();
try {
//noinspection StatementWithEmptyBody
if (Checksum.isAddressWithoutChecksum(address)) {
}
} catch (ArgumentException e) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,108 @@
package com.flashwifi.wifip2p.iotaAPI.Requests;
/**
* Created by Toby on 1/6/2018.
*/
import java.util.ArrayList;
import java.util.List;
import jota.IotaAPI;
import jota.dto.response.GetBalancesResponse;
import jota.dto.response.GetNewAddressResponse;
import jota.error.ArgumentException;
import jota.model.Transaction;
public class WalletAddressAndBalanceChecker {
private static IotaAPI api;
//GetNodeInfoResponse response = api.getNodeInfo();
public WalletAddressAndBalanceChecker() {
//Local node:
//api = new IotaAPI.Builder().build();
//Live node:
/*
api = new IotaAPI.Builder()
.protocol("http")
.host("node.iotawallet.info")
.port("14265")
.build();
*/
//Testnet node:
api = new IotaAPI.Builder()
.protocol("https")
.host("testnet140.tangle.works")
.port("443")
.build();
}
public String getBalance(List<String> inAddresses){
try {
GetBalancesResponse balanceResultResponse = api.getBalances(100, inAddresses);
String[] balanceArray = balanceResultResponse.getBalances();
return balanceArray[balanceArray.length-2];
} catch (ArgumentException | IllegalStateException e) {
e.printStackTrace();
System.out.println("getBalance Error!");
if(e.getMessage().contains("Unable to resolve host")){
System.out.println("Unable to resolve host");
}
}
return null;
}
public List<String> getAddress(String seed) {
Boolean foundAddress = false;
List<String> addressList = new ArrayList<>();
int keyIndex = 0;
System.out.println("GetAddress");
while(foundAddress == false){
System.out.println(keyIndex);
GetNewAddressResponse addressResponse = null;
try {
addressResponse = api.getNewAddress(seed, 2, keyIndex, false, 1, false);
} catch (ArgumentException e) {
e.printStackTrace();
}
if(addressResponse != null) {
addressList.add(addressResponse.getAddresses().get(0));
System.out.println("CurAddress");
System.out.println(addressList.get(0));
String[] addressesCheckArray = new String[1];
addressesCheckArray[0] = addressResponse.getAddresses().get(0);
List<Transaction> transactionsForAddress = null;
try {
transactionsForAddress = api.findTransactionObjectsByAddresses(addressesCheckArray);
} catch (ArgumentException e) {
e.printStackTrace();
//TODO: Handle "Unable to resolve host"
}
if(transactionsForAddress.isEmpty() || (transactionsForAddress.size() == 0 || transactionsForAddress.equals(null))){
//Transactions not found, use this address
foundAddress = true;
System.out.println("No transactions found");
}
else{
//Found transactions, increment for new address
keyIndex+=1;
System.out.println("Found transactions");
}
}
}
return addressList;
}
}

View File

@ -0,0 +1,115 @@
package com.flashwifi.wifip2p.iotaAPI.Requests;
import android.app.Activity;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import jota.IotaAPI;
import jota.dto.response.SendTransferResponse;
import jota.error.ArgumentException;
import jota.model.Transaction;
import jota.model.Transfer;
public class WalletTransferRequest {
private static IotaAPI api;
private static Activity activity;
private String appWalletSeed;
private String sendAddress;
private String sendAmount;
private String message;
private String tag;
private String transferResult;
public WalletTransferRequest(String inSendAddress, String inAppWalletSeed, String inSendAmount, String inMessage, String inTag, Activity inActivity) {
sendAddress = inSendAddress;
appWalletSeed = inAppWalletSeed;
sendAmount = inSendAmount;
message = inMessage;
tag = inTag;
activity = inActivity;
//Local node:
//api = new IotaAPI.Builder().build();
//Live node:
/*
api = new IotaAPI.Builder()
.protocol("http")
.host("node.iotawallet.info")
.port("14265")
.build();
*/
//Testnet node:
api = new IotaAPI.Builder()
.protocol("https")
.host("testnet140.tangle.works")
.port("443")
.build();
}
public String sendRequest(){
List<Transfer> transfers = new ArrayList<>();
System.out.println(sendAddress);
System.out.println(Long.valueOf(sendAmount));
System.out.println("message: "+message);
System.out.println("tag: "+tag);
transfers.add(new Transfer(sendAddress, Long.valueOf(sendAmount), message, tag));
SendTransferResponse sendTransferResponse = null;
try {
System.out.println("=====sendTransfer=====");
//Mainnet
//sendTransferResponse = api.sendTransfer(appWalletSeed, 2, 4, 18, transfers, null, null, false);
//Testnet
sendTransferResponse = api.sendTransfer(appWalletSeed, 2, 4, 9, transfers, null, null, false);
System.out.println("=====sendTransferNextLine=====");
} catch (ArgumentException | IllegalAccessError | IllegalStateException e) {
//e.printStackTrace();
System.out.println("=====Error=====");
if (e instanceof ArgumentException) {
if (e.getMessage().contains("Sending to a used address.") || e.getMessage().contains("Private key reuse detect!") || e.getMessage().contains("Send to inputs!")) {
transferResult = "Sending to a used address/Private key reuse detect. Error Occurred.";
}
else if(e.getMessage().contains("Failed to connect to")){
transferResult = "Failed to connect to";
}
else{
System.out.println("=====E=====");
System.out.println(e);
transferResult = "Network Error: Attaching new address failed or Transaction failed";
}
}
if (e instanceof IllegalAccessError) {
transferResult = "Local POW needs to be enabled";
}
}
if(sendTransferResponse != null){
Boolean[] transferSuccess = sendTransferResponse.getSuccessfully();
//Toast.makeText(getActivity(), "TransferSuccess: "+transferSuccess.toString(), Toast.LENGTH_SHORT).show();
if(transferSuccess.toString().equals("true")){
transferResult = "Sent";
System.out.println("=====Sent=====");
}
}
else{
System.out.println("=====sendResposeNull=====");
}
System.out.println("=====return=====");
return transferResult;
}
}

View File

@ -38,7 +38,6 @@
android:paddingEnd="20dp"
android:paddingStart="40dp"
android:paddingTop="20dp"
android:text="0 i"
android:textColor="#ffffff"
android:textStyle="bold" />
</LinearLayout>

View File

@ -42,7 +42,6 @@
android:paddingEnd="20dp"
android:paddingStart="40dp"
android:paddingTop="20dp"
android:text="0 i"
android:textColor="#ffffff"
android:textStyle="bold" />
@ -98,7 +97,7 @@
android:orientation="vertical">
<EditText
android:id="@+id/editTextAddress"
android:id="@+id/WithdrawWalletAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
@ -116,7 +115,7 @@
android:orientation="horizontal">
<EditText
android:id="@+id/editText3"
android:id="@+id/WithdrawWalletAmount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
@ -129,7 +128,7 @@
android:textColor="#ffffff" />
<TextView
android:id="@+id/FundWalletBalanceValue"
android:id="@+id/WithdrawWalletUnitsLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -146,27 +145,29 @@
</LinearLayout>
<EditText
android:id="@+id/editText4"
android:id="@+id/WithdrawWalletMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginStart="20dp"
android:ems="10"
android:hint="Message (optional, A-Z and 9 only)"
android:inputType="textPersonName"
android:inputType="text"
android:digits="9ABCDEFGHIJKLMNOPQRSTUVWXYZ"
android:paddingBottom="20dp"
android:paddingTop="20dp"
android:textColor="#ffffff" />
<EditText
android:id="@+id/editText5"
android:id="@+id/WithdrawWalletTag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginStart="20dp"
android:ems="10"
android:hint="Tag (optional, A-Z and 9 only)"
android:inputType="textPersonName"
android:inputType="text"
android:digits="9ABCDEFGHIJKLMNOPQRSTUVWXYZ"
android:paddingBottom="20dp"
android:paddingTop="20dp"
android:textColor="#ffffff" />
@ -180,7 +181,7 @@
android:background="#1570FF">
<Button
android:id="@+id/FloatingActionButton"
android:id="@+id/WithdrawWalletSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left"