Manage Your Tokens on the Ethereum Network with Samsung Blockchain Platform SDK
Samiul Hossain
Engineer, Samsung Developer Program
Here at Samsung, we want to stay on top of blockchain technology, which has the potential to revolutionize many aspects of our lives.
Cryptocurrency is a trillion dollar market and one of its biggest players is Ethereum, a blockchain technology that allows its users to create decentralized applications (DApps) powered by its own currency, ETH. The Samsung Blockchain Platform (SBP) SDK provides Android developers with a convenient platform and full set of features for developing DApps that interact with the Ethereum network.
Transactions are the heart of any blockchain-based system. In Ethereum, transactions can be initiated by either an account or a smart contract. The SBP SDK supports ETH transactions, ERC-20 token transactions, and ERC-721 token transactions.
This article describes a sample application that demonstrates how to use the SBP SDK to store and transfer ERC-20 tokens, a primary transaction method on the Ethereum network. You can follow along with the demonstration by downloading the sample application.
The sample application connects to the hardware wallet on the device and retrieves your account details. You can then add a token to the account and create a transaction. Each of these actions has been implemented with a corresponding button to help you understand the sequence of steps and visualize the result.
Figure 1: Sample application
Prerequisites
To implement SBP SDK features in an Android application, you must integrate the SDK by downloading the AAR file and adding its required dependencies.
Retrieving account details
To retrieve your account details:
-
Configure and create an instance of the
SBlockchain
class, which implements the SBP SDK.In the sample application, to call the
OnClickObjectCreate()
method and create an instance of theSBlockchain
class, select Create object.When the instance is successfully created, the message "Object has been created" is displayed.
Figure 2: "SBlockchain" class instance created
-
Connect to the hardware wallet.
Hardware wallets are special devices that provide secure storage for keys. The SBP SDK supports Ledger Nano S, Ledger Nano X, and Samsung Blockchain Keystore wallets. The sample application implements support for the Samsung Blockchain Keystore, which is a hardware wallet preloaded on selected Galaxy devices.
The
SBlockchain
class allows you to access thehardwareWalletManager
class instance, which enables connecting to the hardware wallet.NoteFor information about integrating other hardware wallets, see Getting Started.
In the sample application, when the hardware wallet is connected, its Wallet ID is shown.Figure 3: Hardware wallet connected
-
Restore the account information.
SBP SDK manages each blockchain address as an account. The account contains the information required to sign a transaction, including the public and private key pair. You can both generate accounts for and restore your accounts from the Ethereum network.
Once you have restored your accounts, they are saved and their details can be retrieved requiring a network operation. You only need to restore the accounts again when you change wallets.
NoteFor more information about generating and restoring accounts, see Account Management.
In the sample application, select Create account to restore your account from the network. This is an asynchronous operation. The next time you select "Create account," the account details are simply retrieved from memory.
The sample application retrieves the first account in your HD path and displays its address below the "Create account" button, as shown in Figure 4.Figure 4: Account address successfully retrieved
Sending tokens
To send an ERC-20 token:
-
Add a token to the account.
In the sample application, to add a token to the account, enter the contract address in the "Contract Address" field and select Add token.
NoteTo add the token, you must own it.
For demonstration purposes, a token from the Goerli faucet is added to the account and used in the following figure.Figure 5: Token added
The following code snippet illustrates how the sample application implements adding a token.
public void onClickAddTokenAccount(View view){ if (TextUtils.isEmpty(tokenAddressEditText.getText().toString())) { Toast.makeText(MainActivity.this, "Please add a token address", Toast.LENGTH_SHORT).show(); return; } addedTokenAddress = tokenAddressEditText.getText().toString(); boolean check = checkExistingTokenAccount(); if(check){ Toast.makeText(MainActivity.this, "Token already added", Toast.LENGTH_SHORT).show(); getTokenBalanceButton.setEnabled(true); return; } ethereumService.addTokenAddress((EthereumAccount) mFirstAccount, addedTokenAddress).setCallback(new ListenableFutureTask.Callback<EthereumAccount>() { @Override public void onSuccess(final EthereumAccount account) { Log.d(LOG_TAG, "onSuccess: addTokenAccount " + account); runOnUiThread(()->{ getTokenBalanceButton.setEnabled(true); }); getAccount(); } @Override public void onFailure( @NonNull final ExecutionException exception) { Log.e(LOG_TAG, "onFailure: addTokenAccount " + exception); } @Override public void onCancelled(@NotNull InterruptedException exception) { Log.e(LOG_TAG, "onFailure: addTokenAccount " + exception); } }); } // Check whether the token is already added to the account private boolean checkExistingTokenAccount(){ for (Account currentAccount : accountList) { if (currentAccount.getAddress().equals(mFirstAccount.getAddress())) { if (addedTokenAddress.equals(((EthereumAccount) currentAccount).getTokenAddress())) { return true; } } } return false; }
-
Retrieve the token balance.
To ensure you have sufficient balance to make a transaction, you can check the token balance.
In the sample application, select Get token balance. This is a network operation and can take some time. The retrieved balance is displayed below the "Get token balance" button.
Figure 6: Token balance retrieved
The following code snippet illustrates how to perform this task in two steps: First, retrieve the account associated with the token address with the
getTokenAccount()
method. Next, pass the account into thegetTokenBalance()
method of the SDK to retrieve the token balance.private EthereumAccount getTokenAccount(){ EthereumAccount ethereumAccount = null; for (Account currentAccount : accountList) { if (currentAccount.getAddress().equals(mFirstAccount.getAddress())) { if (addedTokenAddress.equals(((EthereumAccount) currentAccount).getTokenAddress())) { ethereumAccount = (EthereumAccount) currentAccount; } } } return ethereumAccount; } public void onClickGetTokenbalance(View view){ mTokenAccount = getTokenAccount(); if(mTokenAccount == null){ Log.e(LOG_TAG, "Token Account is null"); } ethereumService.getTokenBalance(mTokenAccount).setCallback(new ListenableFutureTask.Callback<BigInteger>() { @Override public void onSuccess(BigInteger tokenBalance) { runOnUiThread(()->{ tokenBalanceText.setEnabled(true); gasPriceButton.setEnabled(true); tokenBalanceText.setText(tokenBalance.toString()); }); } @Override public void onFailure(@NonNull ExecutionException e) { Log.e(LOG_TAG, "Fetching balance is failed."); Log.e(LOG_TAG, "" + e.getMessage()); } @Override public void onCancelled(@NonNull InterruptedException e) { Log.e(LOG_TAG, "Fetching balance is canceled."); } }); }
-
Retrieve the gas fee.
Network operations on the Ethereum network require gas, which is a unit that measures the effort required by the network to complete the operation. To perform a transaction, you must retrieve the gas fee and the gas limit of the transaction. The gas fee is determined by how quickly you want the transaction to be performed.
In the sample application, enter the destination address and amount for the transaction, select the desired transaction speed, then select Gas price.
Figure 7: Gas fee retrieved
The following code snippet illustrates how to retrieve the gas fee.
public void OnClickGasPrice(View view) { ethereumService.getFeeInfo(EthereumTransactionType.EIP1559).setCallback(new ListenableFutureTask.Callback<FeeInfo>() { @Override public void onSuccess(FeeInfo feeInfo) { mEthereumFeeInfo = (Eip1559FeeInfo) feeInfo;; Log.i(LOG_TAG, "Fee info is fetched successfully."); runOnUiThread(() -> { gasLimitButton.setEnabled(true); Toast.makeText(getApplicationContext(), "Fee info is fetched successfully.", Toast.LENGTH_SHORT).show(); } ); } @Override public void onFailure(@NotNull ExecutionException e) { Log.e(LOG_TAG, "Fetching Fee info is failed."); Log.e(LOG_TAG, "" + e.getMessage()); } @Override public void onCancelled(@NotNull InterruptedException e) { Log.e(LOG_TAG, "Fetching Fee info is canceled."); } }); }
-
Retrieve the gas limit.
The gas limit represents the maximum amount of work that may be required for the transaction.
In the sample application, to retrieve the gas limit, select Gas limit. The limit is calculated and displayed next to the "Gas limit" button.
NoteTo retrieve the gas limit, the "Send account address" and "Send amount" fields must not be empty.Figure 8: Gas limit retrieved
The following code snippet illustrates how to retrieve the gas limit.
public void onClickGasLimit(View view) { String toAddress = toAddressEditText.getText().toString(); String amount = sendAmountEditText.getText().toString(); if (toAddress.isEmpty() || amount.isEmpty()) { Toast.makeText(getApplicationContext(), "Fill Send Address and Amount Field", Toast.LENGTH_SHORT).show(); } else if(!ethereumService.isValidAddress(toAddress)){ Toast.makeText(getApplicationContext(), "Invalid Address.", Toast.LENGTH_SHORT).show(); } else { BigDecimal sendAmount = new BigDecimal(amount); BigInteger convertedSendAmount = EthereumUtils.convertEthToWei(sendAmount); List<Type> inParams = Arrays.asList(new Address(toAddress), new Uint(convertedSendAmount)); List outParams = Arrays.asList(new TypeReference<Bool>() {}); String encodedFunction = FunctionEncoder.encode(new Function("transfer", inParams, outParams)); ethereumService.estimateGasLimit((EthereumAccount) mFirstAccount, toAddress, null, encodedFunction).setCallback(new ListenableFutureTask.Callback<BigInteger>() { @Override public void onSuccess(BigInteger bigInteger) { mGasLimit = bigInteger; Log.i(LOG_TAG, "Gas limit is fetched successfully."); Log.i(LOG_TAG, "Gas limit is:" + bigInteger.toString()); runOnUiThread(() -> { sendButton.setEnabled(true); gasLimitText.setText(bigInteger.toString()); }); } @Override public void onFailure(@NotNull ExecutionException e) { Log.e(LOG_TAG, "Fetching Gas limit has failed."); Log.e(LOG_TAG, "" + e.getMessage()); } @Override public void onCancelled(@NotNull InterruptedException e) { Log.e(LOG_TAG, "Fetching Gas limit has been canceled."); } }); } }
-
Send the tokens.
In the sample application, to send tokens using your available balance, select Send.
NoteTo confirm the transaction, Samsung implements a Trusted User Interface (TUI) with the Samsung Blockchain Keystore to ensure that the transaction is safe and not tampered with. The TUI runs completely separate from the device OS, ensuring maximum security. For more information about the TUI and other Samsung Blockchain Keystore features, see What Makes Samsung Blockchain Keystore Unique.
When the transaction is complete, you can use the transaction hash to find the transaction details on the Ethereum blockchain explorer.Figure 9: Transaction created
The following code snippet illustrates how to create a transaction.
public void onClickSendToken(View view) { if (TextUtils.isEmpty(toAddressEditText.getText().toString())) { Toast.makeText(MainActivity.this, "to Address field must be filled!", Toast.LENGTH_SHORT).show(); return; } if (TextUtils.isEmpty(sendAmountEditText.getText().toString())) { Toast.makeText(MainActivity.this, "send amount field must be filled!", Toast.LENGTH_SHORT).show(); return; } String toAddress = toAddressEditText.getText().toString(); BigInteger sendAmount = new BigInteger(sendAmountEditText.getText().toString()); // Retrieve the gas price int transactionSpeedID = transactionSpeedGroup.getCheckedRadioButtonId(); if (transactionSpeedID == -1) { Toast.makeText(getApplicationContext(), "Select a Transaction Speed.", Toast.LENGTH_SHORT).show(); } else { switch (transactionSpeedID) { case R.id.transaction_speed_slow: mGasPrice = mEthereumFeeInfo.getSlowPriorityFee(); Log.d(LOG_TAG, "GasPrice: " + mGasPrice); break; case R.id.transaction_speed_normal: mGasPrice = mEthereumFeeInfo.getNormalPriorityFee(); Log.d(LOG_TAG, "GasPrice: " + mGasPrice); break; case R.id.transaction_speed_fast: mGasPrice = mEthereumFeeInfo.getFastPriorityFee(); Log.d(LOG_TAG, "GasPrice: " + mGasPrice); break; } } if(transactionSpeedID != -1) { try { ethereumService.sendTokenTransaction( mHardwareWallet, (EthereumAccount) mTokenAccount, toAddress, addedTokenAddress, mGasPrice, mEthereumFeeInfo.getEstimatedBaseFee().add(mGasPrice), mGasLimit, sendAmount, null ).setCallback(new ListenableFutureTask.Callback<TransactionResult>() { @Override public void onSuccess(TransactionResult transactionResult) { Log.d(LOG_TAG, "Transaction Hash: " + transactionResult.getHash()); runOnUiThread(() -> Toast.makeText(getApplicationContext(), "Transaction Hash: " + transactionResult.getHash(), Toast.LENGTH_SHORT).show() ); } @Override public void onFailure(@NotNull ExecutionException e) { Log.e(LOG_TAG, "Transaction failed."); Log.e(LOG_TAG, "" + e.getMessage()); } @Override public void onCancelled(@NotNull InterruptedException e) { Log.e(LOG_TAG, "Transaction canceled."); } }); } catch (AvailabilityException e) { Log.e(LOG_TAG, "Error in sending: " + e); } } }
Conclusion
Token transactions are a vital part of the Ethereum ecosystem, and the SBP SDK enables you to create wallet applications that manage ERC-20 tokens and transactions.
Share your thoughts with us and the global blockchain developer community on the Samsung Blockchain Developer Forum.
Learn more about SBP SDK and its features in our recent blogs:
- Integrate Payment UI with Your Ethereum Dapp
- Step into the Decentralized Blockchain Future with Samsung Blockchain Ecosystem
- Send TRX with the Samsung Blockchain Platform SDK
Additional resources on the Samsung Developers site
The Samsung Developers site has many resources for developers looking to build for and integrate with Samsung devices and services. Stay in touch with the latest news by creating a free account and subscribing to our monthly newsletter. Visit the Galaxy Store Games page for information on bringing your game to Galaxy Store and visit the Marketing Resources page for information on promoting and distributing your Android apps. Finally, our Developer Forum is an excellent way to stay up-to-date on all things related to the Galaxy ecosystem.