├── .buildkite └── pipeline.yml ├── .gitignore ├── .scripts └── artifactory.sh ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── one │ │ └── block │ │ └── androidexampleapp │ │ ├── AbiEosInstrumentedTest.java │ │ ├── EosioRpcProviderInstrumentedTest.java │ │ ├── ExampleInstrumentedTest.java │ │ └── TransactionInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── one │ │ │ └── block │ │ │ └── androidexampleapp │ │ │ ├── CheckBalanceTask.java │ │ │ ├── ErrorUtils.java │ │ │ ├── MainActivity.java │ │ │ └── TransactionTask.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── one │ └── block │ └── androidexampleapp │ └── ExampleUnitTest.java ├── build.gradle ├── eosio.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img ├── Android_Robot.png ├── java-logo.png └── screenshot.png └── settings.gradle /.buildkite/pipeline.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | 3 | - command: | 4 | bash .scripts/artifactory.sh 5 | gradle build 6 | label: "Build Library" 7 | agents: 8 | - "queue=automation-android-builder-fleet" 9 | 10 | - command: | 11 | bash .scripts/artifactory.sh 12 | gradle test 13 | label: "Run Tests" 14 | agents: 15 | - "queue=automation-android-builder-fleet" 16 | - wait 17 | 18 | - command: "echo 'Success!!'" 19 | label: ":trophy: Success" 20 | agents: 21 | - "queue=automation-android-builder-fleet" 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | .idea/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | gradle.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # Keystore files 38 | # Uncomment the following line if you do not want to check your keystore files in. 39 | #*.jks 40 | 41 | # External native build folder generated in Android Studio 2.2 and later 42 | .externalNativeBuild 43 | 44 | # Google Services (e.g. APIs or Firebase) 45 | google-services.json 46 | 47 | # Freeline 48 | freeline.py 49 | freeline/ 50 | freeline_project_description.json 51 | 52 | # fastlane 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | fastlane/readme.md 58 | 59 | # Implementtation 60 | *.iml -------------------------------------------------------------------------------- /.scripts/artifactory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu -o pipefail 3 | 4 | GRADLE_PROPERTIES="/var/lib/buildkite-agent/builds/automation-android-builder-fleet/EOSIO/eosio-java-android-example-app/gradle.properties" 5 | export GRADLE_PROPERTIES 6 | echo "Gradle Properties should exist at $GRADLE_PROPERTIES" 7 | echo "Gradle Properties does not exist" 8 | echo "Creating Gradle Properties file..." 9 | touch $GRADLE_PROPERTIES 10 | echo "Writing Secrets to gradle.properties..." 11 | echo "artifactory_username=$(cat /var/lib/buildkite-agent/.artifactory-username)" >> $GRADLE_PROPERTIES 12 | echo "artifactory_password=$(cat /var/lib/buildkite-agent/.artifactory-password)" >> $GRADLE_PROPERTIES 13 | #echo "artifactory_password=testest" >> $GRADLE_PROPERTIES 14 | echo "artifactory_contextURL=https://blockone.jfrog.io/blockone" >> $GRADLE_PROPERTIES 15 | echo "artifactory_repo=android-libs-dev" >> $GRADLE_PROPERTIES 16 | echo "artifactory_path_android_libraries=https://blockone.jfrog.io/blockone/android-libs" >> $GRADLE_PROPERTIES 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | env: 4 | global: 5 | - ANDROID_API=28 6 | - EMULATOR_API=28 7 | - ANDROID_BUILD_TOOLS=28.0.3 8 | - ABI=x86_64 9 | - EMU_FLAVOR=default # use google_apis flavor if no default flavor emulator 10 | - ADB_INSTALL_TIMEOUT=10 # minutes 11 | - ANDROID_HOME=/usr/local/android-sdk 12 | - TOOLS=${ANDROID_HOME}/tools 13 | - PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH} 14 | - VERACODE_APP_NAME=eosio-java-android-example-app 15 | dist: trusty 16 | android: 17 | components: 18 | - tools 19 | - extra-google-google_play_services 20 | - extra-google-m2repository 21 | - extra-android-m2repository 22 | 23 | licenses: 24 | - 'android-sdk-preview-license-.+' 25 | - 'android-sdk-license-.+' 26 | - 'google-gdk-license-.+' 27 | 28 | install: 29 | - touch $HOME/.android/repositories.cfg 30 | - yes | sdkmanager "platform-tools" >/dev/null 31 | - yes | sdkmanager "build-tools;$ANDROID_BUILD_TOOLS" >/dev/null 32 | - yes | sdkmanager "tools" >/dev/null 33 | - yes | sdkmanager "platforms;android-$ANDROID_API" >/dev/null 34 | - yes | sdkmanager "platforms;android-$EMULATOR_API" >/dev/null #for matrix builds, we want to also install the matrix version. 35 | - yes | sdkmanager "system-images;android-$EMULATOR_API;$EMU_FLAVOR;$ABI" >/dev/null 36 | 37 | 38 | before_cache: 39 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 40 | cache: 41 | directories: 42 | - $HOME/.gradle/caches/ 43 | - $HOME/.gradle/wrapper/ 44 | - $HOME/.android/build-cache 45 | 46 | before_script: 47 | - echo no | $ANDROID_HOME/tools/bin/avdmanager create avd --force -n test -k "system-images;android-$EMULATOR_API;$EMU_FLAVOR;$ABI" -c 10M 48 | - emulator -verbose -avd test -no-accel -no-snapshot -no-window $AUDIO -camera-back none -camera-front none -selinux permissive -qemu -m 2048 & 49 | - android-wait-for-emulator 50 | - adb shell input keyevent 82 & 51 | script: 52 | - "./gradlew clean build --stacktrace" 53 | - "./gradlew clean build connectedCheck --stacktrace" 54 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to EOSIO SDK for Java: Android Example App 2 | 3 | Interested in contributing? That's awesome! Here are some guidelines to get started quickly and easily: 4 | 5 | - [Reporting An Issue](#reporting-an-issue) 6 | - [Bug Reports](#bug-reports) 7 | - [Feature Requests](#feature-requests) 8 | - [Change Requests](#change-requests) 9 | - [Working on Android Example App](#working-on-android-example-app) 10 | - [Feature Branches](#feature-branches) 11 | - [Submitting Pull Requests](#submitting-pull-requests) 12 | - [Testing and Quality Assurance](#testing-and-quality-assurance) 13 | - [Conduct](#conduct) 14 | - [Contributor License & Acknowledgments](#contributor-license--acknowledgments) 15 | - [References](#references) 16 | 17 | ## Reporting An Issue 18 | 19 | If you're about to raise an issue because you think you've found a problem with Android Example App, or you'd like to make a request for a new feature in the codebase, or any other reason… please read this first. 20 | 21 | The GitHub issue tracker is the preferred channel for [bug reports](#bug-reports), [feature requests](#feature-requests), and [submitting pull requests](#submitting-pull-requests), but please respect the following restrictions: 22 | 23 | * Please **search for existing issues**. Help us keep duplicate issues to a minimum by checking to see if someone has already reported your problem or requested your idea. 24 | 25 | * Please **be civil**. Keep the discussion on topic and respect the opinions of others. See also our [Contributor Code of Conduct](#conduct). 26 | 27 | ### Bug Reports 28 | 29 | A bug is a _demonstrable problem_ that is caused by the code in the repository. Good bug reports are extremely helpful - thank you! 30 | 31 | Guidelines for bug reports: 32 | 33 | 1. **Use the GitHub issue search** — check if the issue has already been 34 | reported. 35 | 36 | 1. **Check if the issue has been fixed** — look for [closed issues in the 37 | current milestone](/../../issues?q=is%3Aissue+is%3Aclosed) or try to reproduce it 38 | using the latest `develop` branch. 39 | 40 | A good bug report shouldn't leave others needing to chase you up for more information. Be sure to include the details of your environment and relevant tests that demonstrate the failure. 41 | 42 | [Report a bug](/../../issues/new?title=Bug%3A) 43 | 44 | ### Feature Requests 45 | 46 | Feature requests are welcome. Before you submit one be sure to have: 47 | 48 | 1. **Use the GitHub search** and check the feature hasn't already been requested. 49 | 1. Take a moment to think about whether your idea fits with the scope and aims of the project. 50 | 1. Remember, it's up to *you* to make a strong case to convince the project's leaders of the merits of this feature. Please provide as much detail and context as possible, this means explaining the use case and why it is likely to be common. 51 | 52 | ### Change Requests 53 | 54 | Change requests cover both architectural and functional changes to how Android Example App works. If you have an idea for a new or different dependency, a refactor, or an improvement to a feature, etc - please be sure to: 55 | 56 | 1. **Use the GitHub search** and check someone else didn't get there first 57 | 1. Take a moment to think about the best way to make a case for, and explain what you're thinking. Are you sure this shouldn't really be 58 | a [bug report](#bug-reports) or a [feature request](#feature-requests)? Is it really one idea or is it many? What's the context? What problem are you solving? Why is what you are suggesting better than what's already there? 59 | 60 | ## Working on Android Example App 61 | 62 | Code contributions are welcome and encouraged! If you are looking for a good place to start, check out the [good first issue](/../../labels/good%20first%20issue) label in GitHub issues. 63 | 64 | Also, please follow these guidelines when submitting code: 65 | 66 | ### Feature Branches 67 | 68 | To get it out of the way: 69 | 70 | - **[develop](/../../tree/develop)** is the development branch. All work on the next release happens here so you should generally branch off `develop`. Do **NOT** use this branch for a production site. 71 | - **[master](/../../tree/master)** contains the latest release of Android Example App}. This branch may be used in production. Do **NOT** use this branch to work on Android Example App's source. 72 | 73 | ### Submitting Pull Requests 74 | 75 | Pull requests are awesome. If you're looking to raise a PR for something which doesn't have an open issue, please think carefully about [raising an issue](#reporting-an-issue) which your PR can close, especially if you're fixing a bug. This makes it more likely that there will be enough information available for your PR to be properly tested and merged. 76 | 77 | ### Testing and Quality Assurance 78 | 79 | Never underestimate just how useful quality assurance is. If you're looking to get involved with the code base and don't know where to start, checking out and testing a pull request is one of the most useful things you could do. 80 | 81 | Essentially, [check out the latest develop branch](#working-on-android-example-app), take it for a spin, and if you find anything odd, please follow the [bug report guidelines](#bug-reports) and let us know! 82 | 83 | ## Conduct 84 | 85 | While contributing, please be respectful and constructive, so that participation in our project is a positive experience for everyone. 86 | 87 | Examples of behavior that contributes to creating a positive environment include: 88 | - Using welcoming and inclusive language 89 | - Being respectful of differing viewpoints and experiences 90 | - Gracefully accepting constructive criticism 91 | - Focusing on what is best for the community 92 | - Showing empathy towards other community members 93 | 94 | Examples of unacceptable behavior include: 95 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 96 | - Trolling, insulting/derogatory comments, and personal or political attacks 97 | - Public or private harassment 98 | - Publishing others’ private information, such as a physical or electronic address, without explicit permission 99 | - Other conduct which could reasonably be considered inappropriate in a professional setting 100 | 101 | ## Contributor License & Acknowledgments 102 | 103 | Whenever you make a contribution to this project, you license your contribution under the same terms as set out in LICENSE, and you represent and warrant that you have the right to license your contribution under those terms. Whenever you make a contribution to this project, you also certify in the terms of the Developer’s Certificate of Origin set out below: 104 | 105 | ``` 106 | Developer Certificate of Origin 107 | Version 1.1 108 | 109 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 110 | 1 Letterman Drive 111 | Suite D4700 112 | San Francisco, CA, 94129 113 | 114 | Everyone is permitted to copy and distribute verbatim copies of this 115 | license document, but changing it is not allowed. 116 | 117 | 118 | Developer's Certificate of Origin 1.1 119 | 120 | By making a contribution to this project, I certify that: 121 | 122 | (a) The contribution was created in whole or in part by me and I 123 | have the right to submit it under the open source license 124 | indicated in the file; or 125 | 126 | (b) The contribution is based upon previous work that, to the best 127 | of my knowledge, is covered under an appropriate open source 128 | license and I have the right under that license to submit that 129 | work with modifications, whether created in whole or in part 130 | by me, under the same open source license (unless I am 131 | permitted to submit under a different license), as indicated 132 | in the file; or 133 | 134 | (c) The contribution was provided directly to me by some other 135 | person who certified (a), (b) or (c) and I have not modified 136 | it. 137 | 138 | (d) I understand and agree that this project and the contribution 139 | are public and that a record of the contribution (including all 140 | personal information I submit with it, including my sign-off) is 141 | maintained indefinitely and may be redistributed consistent with 142 | this project or the open source license(s) involved. 143 | ``` 144 | 145 | ## References 146 | 147 | * Overall CONTRIB adapted from https://github.com/mathjax/MathJax/blob/master/CONTRIBUTING.md 148 | * Conduct section adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 149 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2019 block.one and its contributors. All rights reserved. 2 | 3 | The MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Java Logo](img/java-logo.png) 2 | # EOSIO SDK for Java: Android Example App 3 | [![Software License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](/./LICENSE) 4 | ![Language Java](https://img.shields.io/badge/Language-Java-yellow.svg) 5 | ![](https://img.shields.io/badge/Deployment%20Target-Android%206%2B-blue.svg) 6 | 7 | The EOSIO SDK for Java: Android Example App is a simple application demonstrating how to integrate with EOSIO-based blockchains using [EOSIO SDK for Java](https://github.com/EOSIO/eosio-java). The application does two things: fetches your account token balance and pushes a transfer action. 8 | 9 | *All product and company names are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.* 10 | 11 | *The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.* 12 | 13 |

14 | 15 |

16 | 17 | ## Contents 18 | 19 | - [Requirements](#requirements) 20 | - [Installation](#installation) 21 | - [About the App](#about-the-app) 22 | - [How to Transact](#how-to-transact) 23 | - [Want to Help?](#want-to-help) 24 | - [License & Legal](#license) 25 | 26 | ## Requirements 27 | 28 | * Android SDK 6.0+ 29 | * Android Studio 3.0+ 30 | * JDK 1.7 31 | 32 | ## Installation 33 | 34 | To get the example application up and running: 35 | 36 | 1. Clone this repo: `git clone https://github.com/EOSIO/eosio-java-android-example-app.git` 37 | 1. Open the project with Android Studio. 38 | 1. Modify `eosio.properties` file: 39 | 40 | ```java 41 | node_url=[your node endpoint URL] //mandatory 42 | from_account=[your account name] //optional 43 | to_account=[receiver account] //optional 44 | from_account_private_key=[your private key] //optional 45 | amount=[amount to transfer] //optional; e.g., 1.1234 EOS 46 | memo=[transfer memo] //optional 47 | private_keys=[for multiple key tests in instrumentation test] //optional 48 | ``` 49 | 50 | `from_account`, `to_account`, `from_account_private_key`, `amount`, `memo` and `private_keys` are optional convenience properties which will prefill the app's form. `node_url` is required. 51 | 52 | 1. Run the app. 53 | 54 | ## About the App 55 | 56 | The app demonstrates how to: 57 | - use the [Java RPC Provider implementation](https://github.com/EOSIO/eosio-java-rpc-provider) to query the chain for an account's token balance, 58 | - get a new transaction from [`TransactionSession`](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/session/TransactionSession.java), 59 | - create an action and add it to a transaction, 60 | - and sign and broadcast the transaction. 61 | 62 | To do this we are using a few libraries and providers, in concert: 63 | 64 | * [EOSIO SDK for Java](https://github.com/EOSIO/eosio-java): The core EOSIO SDK for Java library 65 | * [Java RPC Provider](https://github.com/EOSIO/eosio-java-rpc-provider): An RPC provider implementation for Java (including Android) 66 | * [ABIEOS Serialization Provider for Android](https://github.com/EOSIO/eosio-java-android-abieos-serialization-provider): A pluggable serialization provider for EOSIO SDK for Java using ABIEOS (for transaction and action conversion between JSON and binary data representations) 67 | * [Softkey Signature Provider](https://github.com/EOSIO/eosio-java-softkey-signature-provider): An example pluggable signature provider for EOSIO SDK for Java for signing transactions using in-memory keys (not for production use) 68 | 69 | **WARNING**: [Android 9 disables clear text traffic support by default](https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted) so `android:usesCleartextTraffic` setting in [AndroidManifest.xml](https://github.com/EOSIO/eosio-java-android-example-app/blob/master/app/src/main/AndroidManifest.xml) is used to enable/disable the support depending on the build variant you use. 70 | 71 | ## How to Transact 72 | 73 | [`TransactionTask.java`](app/src/main/java/one/block/androidexampleapp/TransactionTask.java) contains basic sample code for constructing, signing and broadcasting transactions using the `eosiojava` libraries. 74 | 75 | ### Set Up Your TransactionSession 76 | 77 | First, set up your [`TransactionSession`](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/session/TransactionSession.java). This is your factory for creating new transactions: 78 | 79 | 1. Create an instance of the [`AbiEosSerializationProviderImpl`](https://github.com/EOSIO/eosio-java-android-abieos-serialization-provider/blob/develop/eosiojavaabieos/src/main/java/one/block/eosiojavaabieosserializationprovider/AbiEosSerializationProviderImpl.java) serialization provider from the [`eosiojavaandroidabieosserializationprovider`](https://github.com/EOSIO/eosio-java-android-abieos-serialization-provider) library. 80 | 1. Create an instance of the [`EosioJavaRpcProviderImpl`](https://github.com/EOSIO/eosio-java-rpc-provider/blob/master/eosiojavarpcprovider/src/main/java/one/block/eosiojavarpcprovider/implementations/EosioJavaRpcProviderImpl.java) RPC provider with an input string pointing to a nodeos RPC endpoint. You can also use `EosioJavaRpcProviderImpl(String, Boolean)` constructor to enable network debug log. 81 | 1. Create an instance of the [`ABIProviderImpl`](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/implementations/ABIProviderImpl.java) ABI provider, instantiating it with the RPC and serialization provider instances. 82 | 1. Create an instance of the [` SoftKeySignatureProviderImpl`](https://github.com/EOSIO/eosio-java-softkey-signature-provider/blob/master/eosiojavasoftkeysignatureprovider/src/main/java/one/block/eosiosoftkeysignatureprovider/SoftKeySignatureProviderImpl.java) signature provider. (This particular implementation is not recommended for production use due to its simplistic management of private keys). 83 | - Import an EOS private key associated with the sender's account. 84 | 1. Create an instance of [`TransactionSession`](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/session/TransactionSession.java), which is used for spawning [`TransactionProcessor`](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/session/TransactionProcessor.java)s. 85 | 86 | ### Create, Sign and Broadcast Transactions 87 | 88 | Now you're ready to create transactions using your `TransactionSession`: 89 | 90 | 1. Create an instance of [`TransactionProcessor`](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/session/TransactionProcessor.java) from the [`TransactionSession`](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/session/TransactionSession.java) instance above by calling ` TransactionSession#getTransactionProcessor()` or `TransactionSession#getTransactionProcessor(Transaction)`. 91 | 1. Call `TransactionProcessor#prepare(List)` with a list of Actions. The method will serialize the actions, which can always be queried with `Transaction#getActions()`. The transaction now is ready to be signed and broadcast. 92 | 1. Call `TransactionProcessor#signAndBroadcast()` to sign the transaction inside [`TransactionProcessor`](https://github.com/EOSIO/eosio-java/blob/master/eosiojava/src/main/java/one/block/eosiojava/session/TransactionProcessor.java) and broadcast it. 93 | 94 | For a more comprehensive list of available provider implementations, see [`EOSIO SDK for Java - Provider Interface Architecture`](https://github.com/EOSIO/eosio-java/tree/master#provider-interface-architecture). 95 | 96 | For more details about the complete workflow of EOSIO SDK for Java, see [`EOSIO SDK for Java - Complete workflow`](https://github.com/EOSIO/eosio-java/tree/master/documents/complete_workflow.pdf). 97 | 98 | An overview of the error model used in this library can be found in the [`EOSIO SDK for Java - Error Model`](https://github.com/EOSIO/eosio-java/tree/master/documents/error_model.pdf) 99 | 100 | ## Want to help? 101 | 102 | Interested in improving the example application? That's awesome! Here are some [Contribution Guidelines](./CONTRIBUTING.md) and the [Code of Conduct](./CONTRIBUTING.md#conduct). 103 | 104 | If you'd like to contribute to the EOSIO SDK for Java libraries themselves, please see the contribution guidelines on those individual repos. 105 | 106 | ## License 107 | [MIT licensed](./LICENSE) 108 | 109 | ## Important 110 | 111 | See LICENSE for copyright and license terms. Block.one makes its contribution on a voluntary basis as a member of the EOSIO community and is not responsible for ensuring the overall performance of the software or any related applications. We make no representation, warranty, guarantee or undertaking in respect of the software or any related documentation, whether expressed or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall we be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or documentation or the use or other dealings in the software or documentation. Any test results or performance figures are indicative and will not reflect performance under all conditions. Any reference to any third party or third-party product, service or other resource is not an endorsement or recommendation by Block.one. We are not responsible, and disclaim any and all responsibility and liability, for your use of or reliance on any of these resources. Third-party resources may be updated, changed or terminated at any time, so the information here may be out of date or inaccurate. Any person using or offering this software in connection with providing software, goods or services to third parties shall advise such third parties of these license terms, disclaimers and exclusions of liability. Block.one, EOSIO, EOSIO Labs, EOS, the heptahedron and associated logos are trademarks of Block.one. 112 | 113 | Wallets and related components are complex software that require the highest levels of security. If incorrectly built or used, they may compromise users’ private keys and digital assets. Wallet applications and related components should undergo thorough security evaluations before being used. Only experienced developers should work with this software. 114 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | 4 | android { 5 | compileSdkVersion 28 6 | defaultConfig { 7 | applicationId "one.block.androidexampleapp" 8 | minSdkVersion 23 9 | targetSdkVersion 28 10 | versionCode 1 11 | versionName "1.0.0" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | 15 | def Properties properties = new Properties() 16 | properties.load(project.rootProject.file("eosio.properties").newDataInputStream()) 17 | 18 | buildTypes { 19 | debug { 20 | resValue "string", "node_url", properties.getProperty("node_url", "") 21 | resValue "string", "from_account", properties.getProperty("from_account", "") 22 | resValue "string", "to_account", properties.getProperty("to_account", "") 23 | resValue "string", "from_account_private_key", properties.getProperty("from_account_private_key", "") 24 | resValue "string", "amount", properties.getProperty("amount", "") 25 | resValue "string", "memo", properties.getProperty("memo", "") 26 | resValue "string", "private_keys", properties.getProperty("private_keys", "") 27 | 28 | manifestPlaceholders = [usesCleartextTraffic:"true"] 29 | } 30 | release { 31 | resValue "string", "node_url", properties.getProperty("node_url", "") 32 | resValue "string", "from_account", properties.getProperty("from_account", "") 33 | resValue "string", "to_account", properties.getProperty("to_account", "") 34 | resValue "string", "from_account_private_key", properties.getProperty("from_account_private_key", "") 35 | resValue "string", "amount", properties.getProperty("amount", "") 36 | resValue "string", "memo", properties.getProperty("memo", "") 37 | resValue "string", "private_keys", properties.getProperty("private_keys", "") 38 | 39 | manifestPlaceholders = [usesCleartextTraffic:"false"] 40 | 41 | minifyEnabled false 42 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 43 | } 44 | } 45 | 46 | // Needed to get bitcoin-j to produce a valid apk for android. 47 | packagingOptions { 48 | exclude 'lib/x86_64/darwin/libscrypt.dylib' 49 | exclude 'lib/x86_64/freebsd/libscrypt.so' 50 | exclude 'lib/x86_64/linux/libscrypt.so' 51 | } 52 | 53 | compileOptions { 54 | sourceCompatibility JavaVersion.VERSION_1_7 55 | targetCompatibility JavaVersion.VERSION_1_7 56 | } 57 | 58 | lintOptions { 59 | abortOnError false 60 | } 61 | } 62 | 63 | dependencies { 64 | implementation fileTree(include: ['*.jar'], dir: 'libs') 65 | implementation 'one.block:eosiojava:1.0.0' 66 | implementation 'one.block:eosiojavasoftkeysignatureprovider:1.0.0' 67 | implementation 'one.block:eosiojavaandroidabieosserializationprovider:1.0.0' 68 | implementation 'one.block:eosio-java-rpc-provider:1.0.0' 69 | 70 | implementation 'androidx.appcompat:appcompat:1.0.0-beta01' 71 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 72 | testImplementation 'junit:junit:4.12' 73 | androidTestImplementation 'androidx.test:runner:1.1.0-alpha4' 74 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4' 75 | androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1' 76 | implementation 'com.google.android.material:material:1.0.0' 77 | } 78 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/one/block/androidexampleapp/AbiEosInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package one.block.androidexampleapp; 2 | 3 | import org.junit.AfterClass; 4 | import org.junit.BeforeClass; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | import androidx.test.runner.AndroidJUnit4; 9 | import one.block.eosiojava.error.serializationProvider.DeserializeAbiError; 10 | import one.block.eosiojava.error.serializationProvider.DeserializeError; 11 | import one.block.eosiojava.error.serializationProvider.DeserializeTransactionError; 12 | import one.block.eosiojava.error.serializationProvider.SerializationProviderError; 13 | import one.block.eosiojava.error.serializationProvider.SerializeTransactionError; 14 | import one.block.eosiojava.models.AbiEosSerializationObject; 15 | import one.block.eosiojavaabieosserializationprovider.AbiEosSerializationProviderImpl; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertNotNull; 19 | import static org.junit.Assert.fail; 20 | 21 | // At this point its not possible to run these as normal unit tests even though the context 22 | // access to pick up the abi json as resources has been removed. This is because we are 23 | // using the NDK to compile the abieos c++ code, which results in shared libraries that need 24 | // android to run. 25 | 26 | @RunWith(AndroidJUnit4.class) 27 | public class AbiEosInstrumentedTest { 28 | 29 | private static AbiEosSerializationProviderImpl abieos; 30 | 31 | @BeforeClass 32 | public static void startSetup() { 33 | try { 34 | abieos = new AbiEosSerializationProviderImpl(); 35 | } catch (SerializationProviderError serializationProviderError) { 36 | serializationProviderError.printStackTrace(); 37 | fail(); 38 | } 39 | } 40 | 41 | @AfterClass 42 | public static void endTearDown() { 43 | abieos.destroyContext(); 44 | abieos = null; 45 | } 46 | 47 | @Test 48 | public void hexToJsonAbiDef() { 49 | String hex = "0E656F73696F3A3A6162692F312E30010C6163636F756E745F6E616D65046E616D6505087472616E7366657200040466726F6D0C6163636F756E745F6E616D6502746F0C6163636F756E745F6E616D65087175616E74697479056173736574046D656D6F06737472696E67066372656174650002066973737565720C6163636F756E745F6E616D650E6D6178696D756D5F737570706C79056173736574056973737565000302746F0C6163636F756E745F6E616D65087175616E74697479056173736574046D656D6F06737472696E67076163636F756E7400010762616C616E63650561737365740E63757272656E63795F7374617473000306737570706C790561737365740A6D61785F737570706C79056173736574066973737565720C6163636F756E745F6E616D6503000000572D3CCDCD087472616E73666572BC072D2D2D0A7469746C653A20546F6B656E205472616E736665720A73756D6D6172793A205472616E7366657220746F6B656E732066726F6D206F6E65206163636F756E7420746F20616E6F746865722E0A69636F6E3A2068747470733A2F2F63646E2E746573746E65742E6465762E62316F70732E6E65742F746F6B656E2D7472616E736665722E706E6723636535316566396639656563613334333465383535303765306564343965373666666631323635343232626465643032353566333139366561353963386230630A2D2D2D0A0A2323205472616E73666572205465726D73202620436F6E646974696F6E730A0A492C207B7B66726F6D7D7D2C20636572746966792074686520666F6C6C6F77696E6720746F206265207472756520746F207468652062657374206F66206D79206B6E6F776C656467653A0A0A312E204920636572746966792074686174207B7B7175616E746974797D7D206973206E6F74207468652070726F6365656473206F66206672617564756C656E74206F722076696F6C656E7420616374697669746965732E0A322E2049206365727469667920746861742C20746F207468652062657374206F66206D79206B6E6F776C656467652C207B7B746F7D7D206973206E6F7420737570706F7274696E6720696E6974696174696F6E206F662076696F6C656E636520616761696E7374206F74686572732E0A332E2049206861766520646973636C6F73656420616E7920636F6E747261637475616C207465726D73202620636F6E646974696F6E732077697468207265737065637420746F207B7B7175616E746974797D7D20746F207B7B746F7D7D2E0A0A4920756E6465727374616E6420746861742066756E6473207472616E736665727320617265206E6F742072657665727369626C6520616674657220746865207B7B247472616E73616374696F6E2E64656C61795F7365637D7D207365636F6E6473206F72206F746865722064656C617920617320636F6E66696775726564206279207B7B66726F6D7D7D2773207065726D697373696F6E732E0A0A4966207468697320616374696F6E206661696C7320746F20626520697272657665727369626C7920636F6E6669726D656420616674657220726563656976696E6720676F6F6473206F722073657276696365732066726F6D20277B7B746F7D7D272C204920616772656520746F206569746865722072657475726E2074686520676F6F6473206F72207365727669636573206F7220726573656E64207B7B7175616E746974797D7D20696E20612074696D656C79206D616E6E65722E0000000000A531760569737375650000000000A86CD445066372656174650002000000384F4D113203693634010863757272656E6379010675696E743634076163636F756E740000000000904DC603693634010863757272656E6379010675696E7436340E63757272656E63795F737461747300000000"; 50 | String jsonResult = "{\"version\":\"eosio::abi/1.0\",\"types\":[{\"new_type_name\":\"account_name\",\"type\":\"name\"}],\"structs\":[{\"name\":\"transfer\",\"base\":\"\",\"fields\":[{\"name\":\"from\",\"type\":\"account_name\"},{\"name\":\"to\",\"type\":\"account_name\"},{\"name\":\"quantity\",\"type\":\"asset\"},{\"name\":\"memo\",\"type\":\"string\"}]},{\"name\":\"create\",\"base\":\"\",\"fields\":[{\"name\":\"issuer\",\"type\":\"account_name\"},{\"name\":\"maximum_supply\",\"type\":\"asset\"}]},{\"name\":\"issue\",\"base\":\"\",\"fields\":[{\"name\":\"to\",\"type\":\"account_name\"},{\"name\":\"quantity\",\"type\":\"asset\"},{\"name\":\"memo\",\"type\":\"string\"}]},{\"name\":\"account\",\"base\":\"\",\"fields\":[{\"name\":\"balance\",\"type\":\"asset\"}]},{\"name\":\"currency_stats\",\"base\":\"\",\"fields\":[{\"name\":\"supply\",\"type\":\"asset\"},{\"name\":\"max_supply\",\"type\":\"asset\"},{\"name\":\"issuer\",\"type\":\"account_name\"}]}],\"actions\":[{\"name\":\"transfer\",\"type\":\"transfer\",\"ricardian_contract\":\"---\\u000Atitle: Token Transfer\\u000Asummary: Transfer tokens from one account to another.\\u000Aicon: https://cdn.testnet.dev.b1ops.net/token-transfer.png#ce51ef9f9eeca3434e85507e0ed49e76fff1265422bded0255f3196ea59c8b0c\\u000A---\\u000A\\u000A## Transfer Terms & Conditions\\u000A\\u000AI, {{from}}, certify the following to be true to the best of my knowledge:\\u000A\\u000A1. I certify that {{quantity}} is not the proceeds of fraudulent or violent activities.\\u000A2. I certify that, to the best of my knowledge, {{to}} is not supporting initiation of violence against others.\\u000A3. I have disclosed any contractual terms & conditions with respect to {{quantity}} to {{to}}.\\u000A\\u000AI understand that funds transfers are not reversible after the {{$transaction.delay_sec}} seconds or other delay as configured by {{from}}'s permissions.\\u000A\\u000AIf this action fails to be irreversibly confirmed after receiving goods or services from '{{to}}', I agree to either return the goods or services or resend {{quantity}} in a timely manner.\"},{\"name\":\"issue\",\"type\":\"issue\",\"ricardian_contract\":\"\"},{\"name\":\"create\",\"type\":\"create\",\"ricardian_contract\":\"\"}],\"tables\":[{\"name\":\"accounts\",\"index_type\":\"i64\",\"key_names\":[\"currency\"],\"key_types\":[\"uint64\"],\"type\":\"account\"},{\"name\":\"stat\",\"index_type\":\"i64\",\"key_names\":[\"currency\"],\"key_types\":[\"uint64\"],\"type\":\"currency_stats\"}],\"ricardian_clauses\":[],\"error_messages\":[],\"variants\":[],\"action_results\":[],\"kv_tables\":{}}"; 51 | 52 | String json = null; 53 | 54 | try { 55 | json = abieos.deserializeAbi(hex); 56 | } catch (DeserializeAbiError err) { 57 | err.printStackTrace(); 58 | } 59 | 60 | assertNotNull(json); 61 | assertEquals(json, jsonResult); 62 | 63 | } 64 | 65 | @Test 66 | public void hexToJsonAbiTransaction() { 67 | String hex = "AE0D635CDCAC90A6DCFA000000000100A6823403EA3055000000572D3CCDCD0100AEAA4AC15CFD4500000000A8ED32323B00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C69657320686561767900"; 68 | String jsonResult = "{\"expiration\":\"2019-02-12T18:17:18.000\",\"ref_block_num\":44252,\"ref_block_prefix\":4208764560,\"max_net_usage_words\":0,\"max_cpu_usage_ms\":0,\"delay_sec\":0,\"context_free_actions\":[],\"actions\":[{\"account\":\"eosio.token\",\"name\":\"transfer\",\"authorization\":[{\"actor\":\"cryptkeeper\",\"permission\":\"active\"}],\"data\":\"00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C696573206865617679\"}],\"transaction_extensions\":[]}"; 69 | 70 | String json = null; 71 | 72 | try { 73 | json = abieos.deserializeTransaction(hex); 74 | } catch (DeserializeTransactionError err) { 75 | err.printStackTrace(); 76 | } 77 | 78 | assertNotNull(json); 79 | assertEquals(json, jsonResult); 80 | 81 | } 82 | 83 | @Test 84 | public void hexToJsonAbiTokenTransfer() { 85 | String hex = "00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C696573206865617679"; 86 | String jsonResult = "{\"from\":\"cryptkeeper\",\"to\":\"brandon\",\"quantity\":\"42.0000 EOS\",\"memo\":\"the grasshopper lies heavy\"}"; 87 | String abi = "{\"version\":\"eosio::abi/1.0\",\"types\":[{\"new_type_name\":\"account_name\",\"type\":\"name\"}],\"structs\":[{\"name\":\"transfer\",\"base\":\"\",\"fields\":[{\"name\":\"from\",\"type\":\"account_name\"},{\"name\":\"to\",\"type\":\"account_name\"},{\"name\":\"quantity\",\"type\":\"asset\"},{\"name\":\"memo\",\"type\":\"string\"}]},{\"name\":\"create\",\"base\":\"\",\"fields\":[{\"name\":\"issuer\",\"type\":\"account_name\"},{\"name\":\"maximum_supply\",\"type\":\"asset\"}]},{\"name\":\"issue\",\"base\":\"\",\"fields\":[{\"name\":\"to\",\"type\":\"account_name\"},{\"name\":\"quantity\",\"type\":\"asset\"},{\"name\":\"memo\",\"type\":\"string\"}]},{\"name\":\"account\",\"base\":\"\",\"fields\":[{\"name\":\"balance\",\"type\":\"asset\"}]},{\"name\":\"currency_stats\",\"base\":\"\",\"fields\":[{\"name\":\"supply\",\"type\":\"asset\"},{\"name\":\"max_supply\",\"type\":\"asset\"},{\"name\":\"issuer\",\"type\":\"account_name\"}]}],\"actions\":[{\"name\":\"transfer\",\"type\":\"transfer\",\"ricardian_contract\":\"---\\ntitle: Token Transfer\\nsummary: Transfer tokens from one account to another.\\nicon: https://cdn.testnet.dev.b1ops.net/token-transfer.png#ce51ef9f9eeca3434e85507e0ed49e76fff1265422bded0255f3196ea59c8b0c\\n---\\n\\n## Transfer Terms & Conditions\\n\\nI, {{from}}, certify the following to be true to the best of my knowledge:\\n\\n1. I certify that {{quantity}} is not the proceeds of fraudulent or violent activities.\\n2. I certify that, to the best of my knowledge, {{to}} is not supporting initiation of violence against others.\\n3. I have disclosed any contractual terms & conditions with respect to {{quantity}} to {{to}}.\\n\\nI understand that funds transfers are not reversible after the {{$transaction.delay_sec}} seconds or other delay as configured by {{from}}'s permissions.\\n\\nIf this action fails to be irreversibly confirmed after receiving goods or services from '{{to}}', I agree to either return the goods or services or resend {{quantity}} in a timely manner.\"},{\"name\":\"issue\",\"type\":\"issue\",\"ricardian_contract\":\"\"},{\"name\":\"create\",\"type\":\"create\",\"ricardian_contract\":\"\"}],\"tables\":[{\"name\":\"accounts\",\"index_type\":\"i64\",\"key_names\":[\"currency\"],\"key_types\":[\"uint64\"],\"type\":\"account\"},{\"name\":\"stat\",\"index_type\":\"i64\",\"key_names\":[\"currency\"],\"key_types\":[\"uint64\"],\"type\":\"currency_stats\"}],\"ricardian_clauses\":[],\"error_messages\":[],\"abi_extensions\":[],\"variants\":[]}"; 88 | 89 | String json = null; 90 | 91 | try { 92 | AbiEosSerializationObject serializationObject = new AbiEosSerializationObject("eosio.token", "transfer", null, abi); 93 | serializationObject.setHex(hex); 94 | abieos.deserialize(serializationObject); 95 | json = serializationObject.getJson(); 96 | } catch (DeserializeError err) { 97 | err.printStackTrace(); 98 | } 99 | 100 | assertNotNull(json); 101 | assertEquals(json, jsonResult); 102 | 103 | } 104 | 105 | @Test 106 | public void jsonToHexTransaction2() { 107 | String json = "{\n" + 108 | "\"expiration\" : \"2019-02-12T20:35:38.000\",\n" + 109 | "\"ref_block_num\" : 60851,\n" + 110 | "\"ref_block_prefix\" : 1743894440,\n" + 111 | "\"max_net_usage_words\" : 0,\n" + 112 | "\"max_cpu_usage_ms\" : 0,\n" + 113 | "\"delay_sec\" : 0,\n" + 114 | "\"context_free_actions\" : [],\n" + 115 | "\"actions\" : [\n" + 116 | "{\n" + 117 | "\"account\" : \"eosio.assert\",\n" + 118 | "\"name\" : \"require\",\n" + 119 | "\"authorization\" : [],\n" + 120 | "\"data\" : \"CBDD956F52ACD910C3C958136D72F8560D1846BC7CF3157F5FBFB72D3001DE4597F4A1FDBECDA6D59C96A43009FC5E5D7B8F639B1269C77CEC718460DCC19CB30100A6823403EA3055000000572D3CCDCD0143864D5AF0FE294D44D19C612036CBE8C098414C4A12A5A7BB0BFE7DB1556248\"\n" + 121 | "},\n" + 122 | "{\n" + 123 | "\"account\" : \"eosio.token\",\n" + 124 | "\"name\" : \"transfer\",\n" + 125 | "\"authorization\" : [\n" + 126 | "{\n" + 127 | "\"actor\" : \"cryptkeeper\",\n" + 128 | "\"permission\" : \"active\"\n" + 129 | "}\n" + 130 | "],\n" + 131 | "\"data\" : \"00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C696573206865617679\"\n" + 132 | "}\n" + 133 | "]\n" + 134 | ",\n" + 135 | "\"transaction_extensions\" : []\n" + 136 | "}"; 137 | 138 | String hexResult = "1A2E635CB3EDA8B7F167000000000290AFC2D800EA3055000000405DA7ADBA0072CBDD956F52ACD910C3C958136D72F8560D1846BC7CF3157F5FBFB72D3001DE4597F4A1FDBECDA6D59C96A43009FC5E5D7B8F639B1269C77CEC718460DCC19CB30100A6823403EA3055000000572D3CCDCD0143864D5AF0FE294D44D19C612036CBE8C098414C4A12A5A7BB0BFE7DB155624800A6823403EA3055000000572D3CCDCD0100AEAA4AC15CFD4500000000A8ED32323B00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C69657320686561767900"; 139 | 140 | String hex = null; 141 | 142 | try { 143 | hex = abieos.serializeTransaction(json); 144 | } catch (SerializeTransactionError err) { 145 | err.printStackTrace(); 146 | } 147 | 148 | assertNotNull(hex); 149 | assertEquals(hex, hexResult); 150 | } 151 | 152 | @Test 153 | public void testContextError() { 154 | try { 155 | abieos.destroyContext(); 156 | String err = abieos.error(); 157 | fail("Should have thrown an error because the context was null."); 158 | } catch (SerializationProviderError ace) { 159 | 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /app/src/androidTest/java/one/block/androidexampleapp/EosioRpcProviderInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package one.block.androidexampleapp; 2 | 3 | import android.os.AsyncTask; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | import java.io.PrintWriter; 9 | import java.io.StringWriter; 10 | import java.math.BigInteger; 11 | import java.net.SocketTimeoutException; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.concurrent.CountDownLatch; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | import androidx.test.runner.AndroidJUnit4; 18 | import okhttp3.mockwebserver.MockResponse; 19 | import okhttp3.mockwebserver.MockWebServer; 20 | import okhttp3.mockwebserver.SocketPolicy; 21 | import one.block.eosiojava.error.rpcProvider.GetBlockRpcError; 22 | import one.block.eosiojava.models.rpcProvider.Action; 23 | import one.block.eosiojava.models.rpcProvider.Authorization; 24 | import one.block.eosiojava.models.rpcProvider.Transaction; 25 | import one.block.eosiojava.models.rpcProvider.request.GetBlockRequest; 26 | import one.block.eosiojava.models.rpcProvider.request.GetRawAbiRequest; 27 | import one.block.eosiojava.models.rpcProvider.request.GetRequiredKeysRequest; 28 | import one.block.eosiojava.models.rpcProvider.request.PushTransactionRequest; 29 | import one.block.eosiojava.models.rpcProvider.response.GetBlockResponse; 30 | import one.block.eosiojava.models.rpcProvider.response.GetInfoResponse; 31 | import one.block.eosiojava.models.rpcProvider.response.GetRawAbiResponse; 32 | import one.block.eosiojava.models.rpcProvider.response.GetRequiredKeysResponse; 33 | import one.block.eosiojava.models.rpcProvider.response.PushTransactionResponse; 34 | import one.block.eosiojava.models.rpcProvider.response.RPCResponseError; 35 | import one.block.eosiojava.models.rpcProvider.response.RpcError; 36 | import one.block.eosiojavarpcprovider.error.EosioJavaRpcProviderCallError; 37 | import one.block.eosiojavarpcprovider.implementations.EosioJavaRpcProviderImpl; 38 | 39 | import static junit.framework.TestCase.assertEquals; 40 | import static junit.framework.TestCase.assertFalse; 41 | import static junit.framework.TestCase.assertNotNull; 42 | import static junit.framework.TestCase.fail; 43 | import static org.junit.Assert.assertTrue; 44 | 45 | /** 46 | * Instrumented test, which will execute on an Android device. 47 | * 48 | * @see Testing documentation 49 | */ 50 | @RunWith(AndroidJUnit4.class) 51 | public class EosioRpcProviderInstrumentedTest { 52 | 53 | @Test 54 | public void getInfoTest() { 55 | 56 | MockWebServer server = new MockWebServer(); 57 | server.enqueue(new MockResponse().setResponseCode(200).setBody(GET_INFO_RESPONSE)); 58 | 59 | try { 60 | server.start(); 61 | String baseUrl = server.url("/").toString(); 62 | 63 | EosioJavaRpcProviderImpl rpcProvider = new EosioJavaRpcProviderImpl( 64 | baseUrl); 65 | GetInfoResponse response = rpcProvider.getInfo(); 66 | assertNotNull(response); 67 | assertEquals("687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17", 68 | response.getChainId()); 69 | assertEquals("0f6695cb", response.getServerVersion()); 70 | assertEquals("v1.3.0", response.getServerVersionString()); 71 | } catch (Exception ex) { 72 | fail("Should not get exception when calling getInfo(): " + "\n" + getStackTraceString(ex)); 73 | } finally { 74 | try { 75 | server.shutdown(); 76 | } catch (Exception ex) { 77 | // No need for anything here. 78 | } 79 | } 80 | 81 | } 82 | 83 | @Test 84 | public void getBlockTest() { 85 | 86 | MockWebServer server = new MockWebServer(); 87 | server.enqueue(new MockResponse().setResponseCode(200).setBody(GET_BLOCK_RESPONSE)); 88 | 89 | try { 90 | server.start(); 91 | String baseUrl = server.url("/").toString(); 92 | 93 | EosioJavaRpcProviderImpl rpcProvider = new EosioJavaRpcProviderImpl( 94 | baseUrl); 95 | GetBlockRequest request = new GetBlockRequest("25260032"); 96 | GetBlockResponse response = rpcProvider.getBlock(request); 97 | assertNotNull(response); 98 | assertEquals("0181700002e623f2bf291b86a10a5cec4caab4954d4231f31f050f4f86f26116", 99 | response.getId()); 100 | assertEquals(new BigInteger("2249927103"), response.getRefBlockPrefix()); 101 | assertEquals("de5493939e3abdca80deeab2fc9389cc43dc1982708653cfe6b225eb788d6659", 102 | response.getActionMroot()); 103 | } catch (Exception ex) { 104 | fail("Should not get exception when calling getBlock(): " + ex.getLocalizedMessage() 105 | + "\n" + getStackTraceString(ex)); 106 | } finally { 107 | try { 108 | server.shutdown(); 109 | } catch (Exception ex) { 110 | // No need for anything here. 111 | } 112 | } 113 | 114 | } 115 | 116 | @Test 117 | public void getRawAbiTest() { 118 | 119 | MockWebServer server = new MockWebServer(); 120 | server.enqueue(new MockResponse().setResponseCode(200).setBody(GET_RAW_EOSIO_TOKEN_ABI_RESPONSE)); 121 | 122 | try { 123 | server.start(); 124 | String baseUrl = server.url("/").toString(); 125 | 126 | EosioJavaRpcProviderImpl rpcProvider = new EosioJavaRpcProviderImpl( 127 | baseUrl); 128 | GetRawAbiRequest request = new GetRawAbiRequest("eosio.token"); 129 | GetRawAbiResponse response = rpcProvider.getRawAbi(request); 130 | assertNotNull(response); 131 | assertEquals("eosio.token", response.getAccountName()); 132 | assertEquals("43864d5af0fe294d44d19c612036cbe8c098414c4a12a5a7bb0bfe7db1556248", response.getAbiHash()); 133 | assertEquals("DmVvc2lvOjphYmkvMS4wAQxhY2NvdW50X25hbWUEbmFtZQUIdHJhbnNmZXIABARmcm9tDGFjY291bnRfbmFtZQJ0bwxhY2NvdW50X25hbWUIcXVhbnRpdHkFYXNzZXQEbWVtbwZzdHJpbmcGY3JlYXRlAAIGaXNzdWVyDGFjY291bnRfbmFtZQ5tYXhpbXVtX3N1cHBseQVhc3NldAVpc3N1ZQADAnRvDGFjY291bnRfbmFtZQhxdWFudGl0eQVhc3NldARtZW1vBnN0cmluZwdhY2NvdW50AAEHYmFsYW5jZQVhc3NldA5jdXJyZW5jeV9zdGF0cwADBnN1cHBseQVhc3NldAptYXhfc3VwcGx5BWFzc2V0Bmlzc3VlcgxhY2NvdW50X25hbWUDAAAAVy08zc0IdHJhbnNmZXK8By0tLQp0aXRsZTogVG9rZW4gVHJhbnNmZXIKc3VtbWFyeTogVHJhbnNmZXIgdG9rZW5zIGZyb20gb25lIGFjY291bnQgdG8gYW5vdGhlci4KaWNvbjogaHR0cHM6Ly9jZG4udGVzdG5ldC5kZXYuYjFvcHMubmV0L3Rva2VuLXRyYW5zZmVyLnBuZyNjZTUxZWY5ZjllZWNhMzQzNGU4NTUwN2UwZWQ0OWU3NmZmZjEyNjU0MjJiZGVkMDI1NWYzMTk2ZWE1OWM4YjBjCi0tLQoKIyMgVHJhbnNmZXIgVGVybXMgJiBDb25kaXRpb25zCgpJLCB7e2Zyb219fSwgY2VydGlmeSB0aGUgZm9sbG93aW5nIHRvIGJlIHRydWUgdG8gdGhlIGJlc3Qgb2YgbXkga25vd2xlZGdlOgoKMS4gSSBjZXJ0aWZ5IHRoYXQge3txdWFudGl0eX19IGlzIG5vdCB0aGUgcHJvY2VlZHMgb2YgZnJhdWR1bGVudCBvciB2aW9sZW50IGFjdGl2aXRpZXMuCjIuIEkgY2VydGlmeSB0aGF0LCB0byB0aGUgYmVzdCBvZiBteSBrbm93bGVkZ2UsIHt7dG99fSBpcyBub3Qgc3VwcG9ydGluZyBpbml0aWF0aW9uIG9mIHZpb2xlbmNlIGFnYWluc3Qgb3RoZXJzLgozLiBJIGhhdmUgZGlzY2xvc2VkIGFueSBjb250cmFjdHVhbCB0ZXJtcyAmIGNvbmRpdGlvbnMgd2l0aCByZXNwZWN0IHRvIHt7cXVhbnRpdHl9fSB0byB7e3RvfX0uCgpJIHVuZGVyc3RhbmQgdGhhdCBmdW5kcyB0cmFuc2ZlcnMgYXJlIG5vdCByZXZlcnNpYmxlIGFmdGVyIHRoZSB7eyR0cmFuc2FjdGlvbi5kZWxheV9zZWN9fSBzZWNvbmRzIG9yIG90aGVyIGRlbGF5IGFzIGNvbmZpZ3VyZWQgYnkge3tmcm9tfX0ncyBwZXJtaXNzaW9ucy4KCklmIHRoaXMgYWN0aW9uIGZhaWxzIHRvIGJlIGlycmV2ZXJzaWJseSBjb25maXJtZWQgYWZ0ZXIgcmVjZWl2aW5nIGdvb2RzIG9yIHNlcnZpY2VzIGZyb20gJ3t7dG99fScsIEkgYWdyZWUgdG8gZWl0aGVyIHJldHVybiB0aGUgZ29vZHMgb3Igc2VydmljZXMgb3IgcmVzZW5kIHt7cXVhbnRpdHl9fSBpbiBhIHRpbWVseSBtYW5uZXIuAAAAAAClMXYFaXNzdWUAAAAAAKhs1EUGY3JlYXRlAAIAAAA4T00RMgNpNjQBCGN1cnJlbmN5AQZ1aW50NjQHYWNjb3VudAAAAAAAkE3GA2k2NAEIY3VycmVuY3kBBnVpbnQ2NA5jdXJyZW5jeV9zdGF0cwAAAAA==", 134 | response.getAbi()); 135 | } catch (Exception ex) { 136 | fail("Should not get exception when calling getRawAbi(): " + ex.getLocalizedMessage() 137 | + "\n" + getStackTraceString(ex)); 138 | } finally { 139 | try { 140 | server.shutdown(); 141 | } catch (Exception ex) { 142 | // No need for anything here. 143 | } 144 | } 145 | 146 | } 147 | 148 | @Test 149 | public void getRequiredKeysTest() { 150 | 151 | MockWebServer server = new MockWebServer(); 152 | server.enqueue(new MockResponse().setResponseCode(200).setBody(GET_REQUIRED_KEYS_RESPONSE)); 153 | 154 | try { 155 | server.start(); 156 | String baseUrl = server.url("/").toString(); 157 | 158 | EosioJavaRpcProviderImpl rpcProvider = new EosioJavaRpcProviderImpl( 159 | baseUrl); 160 | 161 | GetRequiredKeysRequest request = new GetRequiredKeysRequest(availableKeys(), transactionForRequiredKeys()); 162 | GetRequiredKeysResponse response = rpcProvider.getRequiredKeys(request); 163 | assertNotNull(response); 164 | assertFalse(response.getRequiredKeys().isEmpty()); 165 | assertNotNull(response.getRequiredKeys().get(0)); 166 | assertEquals("EOS5j67P1W2RyBXAL8sNzYcDLox3yLpxyrxgkYy1xsXzVCvzbYpba", 167 | response.getRequiredKeys().get(0)); 168 | } catch (Exception ex) { 169 | fail("Should not get exception when calling getRequiredKeys(): " + ex.getLocalizedMessage() 170 | + "\n" + getStackTraceString(ex)); 171 | } finally { 172 | try { 173 | server.shutdown(); 174 | } catch (Exception ex) { 175 | // No need for anything here. 176 | } 177 | } 178 | 179 | } 180 | 181 | @Test 182 | public void pushTransactionTest() { 183 | 184 | MockWebServer server = new MockWebServer(); 185 | server.enqueue(new MockResponse().setResponseCode(200).setBody(PUSH_TRANSACTION_RESPONSE)); 186 | 187 | try { 188 | server.start(); 189 | String baseUrl = server.url("/").toString(); 190 | 191 | EosioJavaRpcProviderImpl rpcProvider = new EosioJavaRpcProviderImpl( 192 | baseUrl); 193 | 194 | List signatures = new ArrayList<>(); 195 | signatures.add("SIG_K1_JzFA9ffefWfrTBvpwMwZi81kR6tvHF4mfsRekVXrBjLWWikg9g1FrS9WupYuoGaRew5mJhr4d39tHUjHiNCkxamtEfxi68"); 196 | PushTransactionRequest request = new PushTransactionRequest(signatures, 197 | 0, 198 | "", 199 | "C62A4F5C1CEF3D6D71BD000000000290AFC2D800EA3055000000405DA7ADBA0072CBDD956F52ACD910C3C958136D72F8560D1846BC7CF3157F5FBFB72D3001DE4597F4A1FDBECDA6D59C96A43009FC5E5D7B8F639B1269C77CEC718460DCC19CB30100A6823403EA3055000000572D3CCDCD0143864D5AF0FE294D44D19C612036CBE8C098414C4A12A5A7BB0BFE7DB155624800A6823403EA3055000000572D3CCDCD0100AEAA4AC15CFD4500000000A8ED32323B00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C69657320686561767900"); 200 | PushTransactionResponse response = rpcProvider.pushTransaction(request); 201 | assertNotNull(response); 202 | assertEquals("ae735820e26a7b771e1b522186294d7cbba035d0c31ca88237559d6c0a3bf00a", 203 | response.getTransactionId()); 204 | } catch (Exception ex) { 205 | fail("Should not get exception when calling pushTransaction(): " + ex.getLocalizedMessage() 206 | + "\n" + getStackTraceString(ex)); 207 | } finally { 208 | try { 209 | server.shutdown(); 210 | } catch (Exception ex) { 211 | // No need for anything here. 212 | } 213 | } 214 | 215 | } 216 | 217 | @Test 218 | public void pushTransactionErrorTest() { 219 | 220 | MockWebServer server = new MockWebServer(); 221 | server.enqueue(new MockResponse().setResponseCode(500).setBody(PUSH_TRANSACTION_ERROR_RESPONSE)); 222 | 223 | try { 224 | server.start(); 225 | String baseUrl = server.url("/").toString(); 226 | 227 | EosioJavaRpcProviderImpl rpcProvider = new EosioJavaRpcProviderImpl( 228 | baseUrl); 229 | 230 | List signatures = new ArrayList<>(); 231 | signatures.add("SIG_K1_JzFA9ffefWfrTBvpwMwZi81kR6tvHF4mfsRekVXrBjLWWikg9g1FrS9WupYuoGaRew5mJhr4d39tHUjHiNCkxamtEfxi68"); 232 | PushTransactionRequest request = new PushTransactionRequest(signatures, 233 | 0, 234 | "", 235 | "C62A4F5C1CEF3D6D71BD000000000290AFC2D800EA3055000000405DA7ADBA0072CBDD956F52ACD910C3C958136D72F8560D1846BC7CF3157F5FBFB72D3001DE4597F4A1FDBECDA6D59C96A43009FC5E5D7B8F639B1269C77CEC718460DCC19CB30100A6823403EA3055000000572D3CCDCD0143864D5AF0FE294D44D19C612036CBE8C098414C4A12A5A7BB0BFE7DB155624800A6823403EA3055000000572D3CCDCD0100AEAA4AC15CFD4500000000A8ED32323B00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C69657320686561767900"); 236 | PushTransactionResponse response = rpcProvider.pushTransaction(request); 237 | fail("Push transaction should not succeed."); 238 | } catch (Exception ex) { 239 | assertEquals("Error pushing transaction.", ex.getLocalizedMessage()); 240 | assertNotNull(ex.getCause()); 241 | assertEquals("Bad status code: 500 (Server Error), returned from server. Additional error information: See further error information in RPCProviderError.", ex.getCause().getMessage()); 242 | RPCResponseError rpcResponseError = ((EosioJavaRpcProviderCallError)ex.getCause()).getRpcResponseError(); 243 | assertNotNull(rpcResponseError); 244 | assertEquals(new BigInteger("500"), rpcResponseError.getCode()); 245 | assertEquals("Internal Service Error", rpcResponseError.getMessage()); 246 | RpcError rpcError = rpcResponseError.getError(); 247 | assertNotNull(rpcError); 248 | assertEquals(new BigInteger("3040005"), rpcError.getCode()); 249 | assertEquals("Expired Transaction", rpcError.getWhat()); 250 | } finally { 251 | try { 252 | server.shutdown(); 253 | } catch (Exception ex) { 254 | // No need for anything here. 255 | } 256 | } 257 | 258 | } 259 | 260 | @Test 261 | public void getInfoTimeoutTest() { 262 | 263 | MockWebServer server = new MockWebServer(); 264 | MockResponse mockResponse = new MockResponse().setResponseCode(200).setBody(GET_INFO_RESPONSE); 265 | // This will cause the call to time out. 266 | mockResponse.setSocketPolicy(SocketPolicy.NO_RESPONSE); 267 | server.enqueue(mockResponse); 268 | 269 | try { 270 | server.start(); 271 | String baseUrl = server.url("/").toString(); 272 | 273 | EosioJavaRpcProviderImpl rpcProvider = new EosioJavaRpcProviderImpl( 274 | baseUrl); 275 | GetInfoResponse response = rpcProvider.getInfo(); 276 | fail("Should not succeed when calling getInfo(). Should time out."); 277 | } catch (Exception ex) { 278 | assertEquals("Error retrieving chain information.", ex.getMessage()); 279 | assertNotNull(ex.getCause()); 280 | assertTrue(ex.getCause() instanceof SocketTimeoutException); 281 | assertEquals("timeout", ex.getCause().getMessage()); 282 | } finally { 283 | try { 284 | server.shutdown(); 285 | } catch (Exception ex) { 286 | // No need for anything here. 287 | } 288 | } 289 | 290 | } 291 | 292 | @Test 293 | public void getBlockAsyncTest() { 294 | 295 | // This test shows how an RPC provider call might be made asynchronously. 296 | 297 | final CountDownLatch testLock = new CountDownLatch(1); 298 | 299 | MockWebServer server = new MockWebServer(); 300 | server.enqueue(new MockResponse().setResponseCode(200).setBody(GET_BLOCK_RESPONSE)); 301 | 302 | try { 303 | server.start(); 304 | String baseUrl = server.url("/").toString(); 305 | 306 | final EosioJavaRpcProviderImpl rpcProvider = new EosioJavaRpcProviderImpl( 307 | baseUrl); 308 | GetBlockRequest[] request = { new GetBlockRequest("25260032") }; 309 | 310 | AsyncTask asyncTask = new AsyncTask() { 311 | GetBlockRpcError getBlockError = null; 312 | @Override 313 | protected GetBlockResponse doInBackground(GetBlockRequest... getBlockRequests) { 314 | // Here we are on a background thread. 315 | GetBlockResponse response = null; 316 | try { 317 | response = rpcProvider.getBlock(getBlockRequests[0]); 318 | } catch (GetBlockRpcError err) { 319 | getBlockError = err; 320 | } 321 | return response; 322 | } 323 | 324 | protected void onPostExecute(GetBlockResponse response) { 325 | // Here we are back on the main thread and could update the UI. 326 | assertNotNull(response); 327 | assertEquals("0181700002e623f2bf291b86a10a5cec4caab4954d4231f31f050f4f86f26116", 328 | response.getId()); 329 | assertEquals(new BigInteger("2249927103"), response.getRefBlockPrefix()); 330 | assertEquals("de5493939e3abdca80deeab2fc9389cc43dc1982708653cfe6b225eb788d6659", 331 | response.getActionMroot()); 332 | testLock.countDown(); 333 | } 334 | }.execute(request); 335 | 336 | try { 337 | testLock.await(5000, TimeUnit.MILLISECONDS); 338 | } catch (InterruptedException interruptedException) { 339 | fail("Interrupted waiting for getBlock() to complete: " + 340 | interruptedException.getLocalizedMessage()); 341 | } 342 | 343 | } catch (Exception ex) { 344 | fail("Should not get exception when calling getBlock(): " + ex.getLocalizedMessage()); 345 | } finally { 346 | try { 347 | server.shutdown(); 348 | } catch (Exception ex) { 349 | // No need for anything here. 350 | } 351 | } 352 | 353 | } 354 | 355 | private String getStackTraceString(Exception ex) { 356 | StringWriter stringWriter = new StringWriter(); 357 | PrintWriter printWriter = new PrintWriter(stringWriter); 358 | ex.printStackTrace(printWriter); 359 | return stringWriter.toString(); 360 | } 361 | 362 | private List availableKeys() { 363 | 364 | List availableKeys = new ArrayList<>(); 365 | availableKeys.add("PUB_K1_5j67P1W2RyBXAL8sNzYcDLox3yLpxyrxgkYy1xsXzVCw1oi9eG"); 366 | return availableKeys; 367 | 368 | } 369 | 370 | private Transaction transactionForRequiredKeys() { 371 | List authList = new ArrayList<>(); 372 | authList.add(new Authorization("cryptkeeper", "active")); 373 | 374 | List actionList = new ArrayList<>(); 375 | Action action = new Action("eosio.token", 376 | "transfer", 377 | authList, 378 | "00AEAA4AC15CFD4500000060D234CD3DA06806000000000004454F53000000001A746865206772617373686F70706572206C696573206865617679" 379 | ); 380 | actionList.add(action); 381 | 382 | Transaction transaction = new Transaction( 383 | "2019-01-25T22:13:55", 384 | new BigInteger("44503"), 385 | new BigInteger("1776994640"), 386 | BigInteger.ZERO, 387 | BigInteger.ZERO, 388 | BigInteger.ZERO, 389 | new ArrayList(), 390 | actionList, 391 | new ArrayList() 392 | ); 393 | 394 | return transaction; 395 | 396 | } 397 | 398 | private static final String GET_INFO_RESPONSE = "{\n" 399 | + " \"server_version\": \"0f6695cb\",\n" 400 | + " \"chain_id\": \"687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17\",\n" 401 | + " \"head_block_num\": 20583056,\n" 402 | + " \"last_irreversible_block_num\": 20583039,\n" 403 | + " \"last_irreversible_block_id\": \"013a127fab9a79403a20b55914cdc7e1ac136618387325ad3c1914d27528a1f1\",\n" 404 | + " \"head_block_id\": \"013a129048f4486ce8a5ac8380870a8ce1bcbd4ff45b40fd0792503dc44c427d\",\n" 405 | + " \"head_block_time\": \"2019-01-25T16:39:38.000\",\n" 406 | + " \"head_block_producer\": \"blkproducer1\",\n" 407 | + " \"virtual_block_cpu_limit\": 200000000,\n" 408 | + " \"virtual_block_net_limit\": 1048576000,\n" 409 | + " \"block_cpu_limit\": 199900,\n" 410 | + " \"block_net_limit\": 1048576,\n" 411 | + " \"server_version_string\": \"v1.3.0\"\n" 412 | + "}"; 413 | 414 | private static final String GET_BLOCK_RESPONSE = "{\n" 415 | + " \"timestamp\": \"2019-02-21T18:31:40.000\",\n" 416 | + " \"producer\": \"blkproducer2\",\n" 417 | + " \"confirmed\": 0,\n" 418 | + " \"previous\": \"01816fffa4548475add3c45d0e0620f59468a6817426137b37851c23ccafa9cc\",\n" 419 | + " \"transaction_mroot\": \"0000000000000000000000000000000000000000000000000000000000000000\",\n" 420 | + " \"action_mroot\": \"de5493939e3abdca80deeab2fc9389cc43dc1982708653cfe6b225eb788d6659\",\n" 421 | + " \"schedule_version\": 3,\n" 422 | + " \"new_producers\": null,\n" 423 | + " \"header_extensions\": [],\n" 424 | + " \"producer_signature\": \"SIG_K1_KZ3ptku7orAgcyMzd9FKW4jPC9PvjW9BGadFoyxdJFWM44VZdjW28DJgDe6wkNHAxnpqCWSzaBHB1AfbXBUn3HDzetemoA\",\n" 425 | + " \"transactions\": [],\n" 426 | + " \"block_extensions\": [],\n" 427 | + " \"id\": \"0181700002e623f2bf291b86a10a5cec4caab4954d4231f31f050f4f86f26116\",\n" 428 | + " \"block_num\": 25260032,\n" 429 | + " \"ref_block_prefix\": 2249927103\n" 430 | + "}"; 431 | 432 | private static final String GET_RAW_EOSIO_TOKEN_ABI_RESPONSE = "{\n" 433 | + " \"account_name\": \"eosio.token\",\n" 434 | + " \"code_hash\": \"3e0cf4172ab025f9fff5f1db11ee8a34d44779492e1d668ae1dc2d129e865348\",\n" 435 | + " \"abi_hash\": \"43864d5af0fe294d44d19c612036cbe8c098414c4a12a5a7bb0bfe7db1556248\",\n" 436 | + " \"abi\": \"DmVvc2lvOjphYmkvMS4wAQxhY2NvdW50X25hbWUEbmFtZQUIdHJhbnNmZXIABARmcm9tDGFjY291bnRfbmFtZQJ0bwxhY2NvdW50X25hbWUIcXVhbnRpdHkFYXNzZXQEbWVtbwZzdHJpbmcGY3JlYXRlAAIGaXNzdWVyDGFjY291bnRfbmFtZQ5tYXhpbXVtX3N1cHBseQVhc3NldAVpc3N1ZQADAnRvDGFjY291bnRfbmFtZQhxdWFudGl0eQVhc3NldARtZW1vBnN0cmluZwdhY2NvdW50AAEHYmFsYW5jZQVhc3NldA5jdXJyZW5jeV9zdGF0cwADBnN1cHBseQVhc3NldAptYXhfc3VwcGx5BWFzc2V0Bmlzc3VlcgxhY2NvdW50X25hbWUDAAAAVy08zc0IdHJhbnNmZXK8By0tLQp0aXRsZTogVG9rZW4gVHJhbnNmZXIKc3VtbWFyeTogVHJhbnNmZXIgdG9rZW5zIGZyb20gb25lIGFjY291bnQgdG8gYW5vdGhlci4KaWNvbjogaHR0cHM6Ly9jZG4udGVzdG5ldC5kZXYuYjFvcHMubmV0L3Rva2VuLXRyYW5zZmVyLnBuZyNjZTUxZWY5ZjllZWNhMzQzNGU4NTUwN2UwZWQ0OWU3NmZmZjEyNjU0MjJiZGVkMDI1NWYzMTk2ZWE1OWM4YjBjCi0tLQoKIyMgVHJhbnNmZXIgVGVybXMgJiBDb25kaXRpb25zCgpJLCB7e2Zyb219fSwgY2VydGlmeSB0aGUgZm9sbG93aW5nIHRvIGJlIHRydWUgdG8gdGhlIGJlc3Qgb2YgbXkga25vd2xlZGdlOgoKMS4gSSBjZXJ0aWZ5IHRoYXQge3txdWFudGl0eX19IGlzIG5vdCB0aGUgcHJvY2VlZHMgb2YgZnJhdWR1bGVudCBvciB2aW9sZW50IGFjdGl2aXRpZXMuCjIuIEkgY2VydGlmeSB0aGF0LCB0byB0aGUgYmVzdCBvZiBteSBrbm93bGVkZ2UsIHt7dG99fSBpcyBub3Qgc3VwcG9ydGluZyBpbml0aWF0aW9uIG9mIHZpb2xlbmNlIGFnYWluc3Qgb3RoZXJzLgozLiBJIGhhdmUgZGlzY2xvc2VkIGFueSBjb250cmFjdHVhbCB0ZXJtcyAmIGNvbmRpdGlvbnMgd2l0aCByZXNwZWN0IHRvIHt7cXVhbnRpdHl9fSB0byB7e3RvfX0uCgpJIHVuZGVyc3RhbmQgdGhhdCBmdW5kcyB0cmFuc2ZlcnMgYXJlIG5vdCByZXZlcnNpYmxlIGFmdGVyIHRoZSB7eyR0cmFuc2FjdGlvbi5kZWxheV9zZWN9fSBzZWNvbmRzIG9yIG90aGVyIGRlbGF5IGFzIGNvbmZpZ3VyZWQgYnkge3tmcm9tfX0ncyBwZXJtaXNzaW9ucy4KCklmIHRoaXMgYWN0aW9uIGZhaWxzIHRvIGJlIGlycmV2ZXJzaWJseSBjb25maXJtZWQgYWZ0ZXIgcmVjZWl2aW5nIGdvb2RzIG9yIHNlcnZpY2VzIGZyb20gJ3t7dG99fScsIEkgYWdyZWUgdG8gZWl0aGVyIHJldHVybiB0aGUgZ29vZHMgb3Igc2VydmljZXMgb3IgcmVzZW5kIHt7cXVhbnRpdHl9fSBpbiBhIHRpbWVseSBtYW5uZXIuAAAAAAClMXYFaXNzdWUAAAAAAKhs1EUGY3JlYXRlAAIAAAA4T00RMgNpNjQBCGN1cnJlbmN5AQZ1aW50NjQHYWNjb3VudAAAAAAAkE3GA2k2NAEIY3VycmVuY3kBBnVpbnQ2NA5jdXJyZW5jeV9zdGF0cwAAAAA==\"\n" 437 | + "}"; 438 | 439 | 440 | private static final String GET_REQUIRED_KEYS_RESPONSE = "{\n" 441 | + " \"required_keys\": [\n" 442 | + " \"EOS5j67P1W2RyBXAL8sNzYcDLox3yLpxyrxgkYy1xsXzVCvzbYpba\"\n" 443 | + " ]\n" 444 | + "}"; 445 | 446 | private static final String PUSH_TRANSACTION_RESPONSE = "{\n" 447 | + " \"transaction_id\": \"ae735820e26a7b771e1b522186294d7cbba035d0c31ca88237559d6c0a3bf00a\",\n" 448 | + " \"processed\": {\n" 449 | + " \"id\": \"ae735820e26a7b771e1b522186294d7cbba035d0c31ca88237559d6c0a3bf00a\",\n" 450 | + " \"block_num\": 21098575,\n" 451 | + " \"block_time\": \"2019-01-28T16:15:37.500\",\n" 452 | + " \"producer_block_id\": null,\n" 453 | + " \"receipt\": {\n" 454 | + " \"status\": \"executed\",\n" 455 | + " \"cpu_usage_us\": 3837,\n" 456 | + " \"net_usage_words\": 36\n" 457 | + " },\n" 458 | + " \"elapsed\": 3837,\n" 459 | + " \"net_usage\": 288,\n" 460 | + " \"scheduled\": false,\n" 461 | + " \"action_traces\": [\n" 462 | + " {\n" 463 | + " \"receipt\": {\n" 464 | + " \"receiver\": \"eosio.assert\",\n" 465 | + " \"act_digest\": \"a4caeedd5e5824dd916c1aaabc84f0a114ddbda83728c8c23ba859d4a8a93721\",\n" 466 | + " \"global_sequence\": 21103875,\n" 467 | + " \"recv_sequence\": 332,\n" 468 | + " \"auth_sequence\": [],\n" 469 | + " \"code_sequence\": 1,\n" 470 | + " \"abi_sequence\": 1\n" 471 | + " },\n" 472 | + " \"act\": {\n" 473 | + " \"account\": \"eosio.assert\",\n" 474 | + " \"name\": \"require\",\n" 475 | + " \"authorization\": [],\n" 476 | + " \"data\": {\n" 477 | + " \"chain_params_hash\": \"cbdd956f52acd910c3c958136d72f8560d1846bc7cf3157f5fbfb72d3001de45\",\n" 478 | + " \"manifest_id\": \"97f4a1fdbecda6d59c96a43009fc5e5d7b8f639b1269c77cec718460dcc19cb3\",\n" 479 | + " \"actions\": [\n" 480 | + " {\n" 481 | + " \"contract\": \"eosio.token\",\n" 482 | + " \"action\": \"transfer\"\n" 483 | + " }\n" 484 | + " ],\n" 485 | + " \"abi_hashes\": [\n" 486 | + " \"43864d5af0fe294d44d19c612036cbe8c098414c4a12a5a7bb0bfe7db1556248\"\n" 487 | + " ]\n" 488 | + " },\n" 489 | + " \"hex_data\": \"cbdd956f52acd910c3c958136d72f8560d1846bc7cf3157f5fbfb72d3001de4597f4a1fdbecda6d59c96a43009fc5e5d7b8f639b1269c77cec718460dcc19cb30100a6823403ea3055000000572d3ccdcd0143864d5af0fe294d44d19c612036cbe8c098414c4a12a5a7bb0bfe7db1556248\"\n" 490 | + " },\n" 491 | + " \"context_free\": false,\n" 492 | + " \"elapsed\": 1264,\n" 493 | + " \"cpu_usage\": 0,\n" 494 | + " \"console\": \"\",\n" 495 | + " \"total_cpu_usage\": 0,\n" 496 | + " \"trx_id\": \"ae735820e26a7b771e1b522186294d7cbba035d0c31ca88237559d6c0a3bf00a\",\n" 497 | + " \"block_num\": 21098575,\n" 498 | + " \"block_time\": \"2019-01-28T16:15:37.500\",\n" 499 | + " \"producer_block_id\": null,\n" 500 | + " \"account_ram_deltas\": [],\n" 501 | + " \"inline_traces\": []\n" 502 | + " },\n" 503 | + " {\n" 504 | + " \"receipt\": {\n" 505 | + " \"receiver\": \"eosio.token\",\n" 506 | + " \"act_digest\": \"9eab239d66d13c34b9cc35a6f79fb2f6d61a2d9df9a484075c82e65d73a0cbc8\",\n" 507 | + " \"global_sequence\": 21103876,\n" 508 | + " \"recv_sequence\": 1366,\n" 509 | + " \"auth_sequence\": [\n" 510 | + " [\n" 511 | + " \"cryptkeeper\",\n" 512 | + " 875\n" 513 | + " ]\n" 514 | + " ],\n" 515 | + " \"code_sequence\": 1,\n" 516 | + " \"abi_sequence\": 4\n" 517 | + " },\n" 518 | + " \"act\": {\n" 519 | + " \"account\": \"eosio.token\",\n" 520 | + " \"name\": \"transfer\",\n" 521 | + " \"authorization\": [\n" 522 | + " {\n" 523 | + " \"actor\": \"cryptkeeper\",\n" 524 | + " \"permission\": \"active\"\n" 525 | + " }\n" 526 | + " ],\n" 527 | + " \"data\": {\n" 528 | + " \"from\": \"cryptkeeper\",\n" 529 | + " \"to\": \"brandon\",\n" 530 | + " \"quantity\": \"42.0000 EOS\",\n" 531 | + " \"memo\": \"the grasshopper lies heavy\"\n" 532 | + " },\n" 533 | + " \"hex_data\": \"00aeaa4ac15cfd4500000060d234cd3da06806000000000004454f53000000001a746865206772617373686f70706572206c696573206865617679\"\n" 534 | + " },\n" 535 | + " \"context_free\": false,\n" 536 | + " \"elapsed\": 2197,\n" 537 | + " \"cpu_usage\": 0,\n" 538 | + " \"console\": \"\",\n" 539 | + " \"total_cpu_usage\": 0,\n" 540 | + " \"trx_id\": \"ae735820e26a7b771e1b522186294d7cbba035d0c31ca88237559d6c0a3bf00a\",\n" 541 | + " \"block_num\": 21098575,\n" 542 | + " \"block_time\": \"2019-01-28T16:15:37.500\",\n" 543 | + " \"producer_block_id\": null,\n" 544 | + " \"account_ram_deltas\": [],\n" 545 | + " \"inline_traces\": [\n" 546 | + " {\n" 547 | + " \"receipt\": {\n" 548 | + " \"receiver\": \"cryptkeeper\",\n" 549 | + " \"act_digest\": \"9eab239d66d13c34b9cc35a6f79fb2f6d61a2d9df9a484075c82e65d73a0cbc8\",\n" 550 | + " \"global_sequence\": 21103877,\n" 551 | + " \"recv_sequence\": 496,\n" 552 | + " \"auth_sequence\": [\n" 553 | + " [\n" 554 | + " \"cryptkeeper\",\n" 555 | + " 876\n" 556 | + " ]\n" 557 | + " ],\n" 558 | + " \"code_sequence\": 1,\n" 559 | + " \"abi_sequence\": 4\n" 560 | + " },\n" 561 | + " \"act\": {\n" 562 | + " \"account\": \"eosio.token\",\n" 563 | + " \"name\": \"transfer\",\n" 564 | + " \"authorization\": [\n" 565 | + " {\n" 566 | + " \"actor\": \"cryptkeeper\",\n" 567 | + " \"permission\": \"active\"\n" 568 | + " }\n" 569 | + " ],\n" 570 | + " \"data\": {\n" 571 | + " \"from\": \"cryptkeeper\",\n" 572 | + " \"to\": \"brandon\",\n" 573 | + " \"quantity\": \"42.0000 EOS\",\n" 574 | + " \"memo\": \"the grasshopper lies heavy\"\n" 575 | + " },\n" 576 | + " \"hex_data\": \"00aeaa4ac15cfd4500000060d234cd3da06806000000000004454f53000000001a746865206772617373686f70706572206c696573206865617679\"\n" 577 | + " },\n" 578 | + " \"context_free\": false,\n" 579 | + " \"elapsed\": 6,\n" 580 | + " \"cpu_usage\": 0,\n" 581 | + " \"console\": \"\",\n" 582 | + " \"total_cpu_usage\": 0,\n" 583 | + " \"trx_id\": \"ae735820e26a7b771e1b522186294d7cbba035d0c31ca88237559d6c0a3bf00a\",\n" 584 | + " \"block_num\": 21098575,\n" 585 | + " \"block_time\": \"2019-01-28T16:15:37.500\",\n" 586 | + " \"producer_block_id\": null,\n" 587 | + " \"account_ram_deltas\": [],\n" 588 | + " \"inline_traces\": []\n" 589 | + " },\n" 590 | + " {\n" 591 | + " \"receipt\": {\n" 592 | + " \"receiver\": \"brandon\",\n" 593 | + " \"act_digest\": \"9eab239d66d13c34b9cc35a6f79fb2f6d61a2d9df9a484075c82e65d73a0cbc8\",\n" 594 | + " \"global_sequence\": 21103878,\n" 595 | + " \"recv_sequence\": 582,\n" 596 | + " \"auth_sequence\": [\n" 597 | + " [\n" 598 | + " \"cryptkeeper\",\n" 599 | + " 877\n" 600 | + " ]\n" 601 | + " ],\n" 602 | + " \"code_sequence\": 1,\n" 603 | + " \"abi_sequence\": 4\n" 604 | + " },\n" 605 | + " \"act\": {\n" 606 | + " \"account\": \"eosio.token\",\n" 607 | + " \"name\": \"transfer\",\n" 608 | + " \"authorization\": [\n" 609 | + " {\n" 610 | + " \"actor\": \"cryptkeeper\",\n" 611 | + " \"permission\": \"active\"\n" 612 | + " }\n" 613 | + " ],\n" 614 | + " \"data\": {\n" 615 | + " \"from\": \"cryptkeeper\",\n" 616 | + " \"to\": \"brandon\",\n" 617 | + " \"quantity\": \"42.0000 EOS\",\n" 618 | + " \"memo\": \"the grasshopper lies heavy\"\n" 619 | + " },\n" 620 | + " \"hex_data\": \"00aeaa4ac15cfd4500000060d234cd3da06806000000000004454f53000000001a746865206772617373686f70706572206c696573206865617679\"\n" 621 | + " },\n" 622 | + " \"context_free\": false,\n" 623 | + " \"elapsed\": 5,\n" 624 | + " \"cpu_usage\": 0,\n" 625 | + " \"console\": \"\",\n" 626 | + " \"total_cpu_usage\": 0,\n" 627 | + " \"trx_id\": \"ae735820e26a7b771e1b522186294d7cbba035d0c31ca88237559d6c0a3bf00a\",\n" 628 | + " \"block_num\": 21098575,\n" 629 | + " \"block_time\": \"2019-01-28T16:15:37.500\",\n" 630 | + " \"producer_block_id\": null,\n" 631 | + " \"account_ram_deltas\": [],\n" 632 | + " \"inline_traces\": []\n" 633 | + " }\n" 634 | + " ]\n" 635 | + " }\n" 636 | + " ],\n" 637 | + " \"except\": null\n" 638 | + " }\n" 639 | + "}"; 640 | 641 | private static final String PUSH_TRANSACTION_ERROR_RESPONSE = "{\n" 642 | + " \"code\": 500,\n" 643 | + " \"message\": \"Internal Service Error\",\n" 644 | + " \"error\": {\n" 645 | + " \"code\": 3040005,\n" 646 | + " \"name\": \"expired_tx_exception\",\n" 647 | + " \"what\": \"Expired Transaction\",\n" 648 | + " \"details\": [\n" 649 | + " {\n" 650 | + " \"message\": \"expired transaction ae735820e26a7b771e1b522186294d7cbba035d0c31ca88237559d6c0a3bf00a\",\n" 651 | + " \"file\": \"producer_plugin.cpp\",\n" 652 | + " \"line_number\": 378,\n" 653 | + " \"method\": \"on_incoming_transaction_async\"\n" 654 | + " }\n" 655 | + " ]\n" 656 | + " }\n" 657 | + "}"; 658 | } 659 | -------------------------------------------------------------------------------- /app/src/androidTest/java/one/block/androidexampleapp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package one.block.androidexampleapp; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.InstrumentationRegistry; 6 | import androidx.test.runner.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getTargetContext(); 24 | 25 | assertEquals("one.block.androidexampleapp", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/androidTest/java/one/block/androidexampleapp/TransactionInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package one.block.androidexampleapp; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.InstrumentationRegistry; 6 | import androidx.test.runner.AndroidJUnit4; 7 | 8 | import org.json.JSONArray; 9 | import org.json.JSONException; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | 13 | import java.util.concurrent.CountDownLatch; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | import static org.junit.Assert.assertTrue; 17 | 18 | @RunWith(AndroidJUnit4.class) 19 | public class TransactionInstrumentedTest { 20 | 21 | @Test 22 | public void testSubmitTransaction() throws JSONException, InterruptedException { 23 | Context appContext = InstrumentationRegistry.getTargetContext(); 24 | String nodeURL = appContext.getString(R.string.node_url); 25 | 26 | String fromAccount = appContext.getString(R.string.from_account); 27 | String toAccount = appContext.getString(R.string.to_account); 28 | String amount = appContext.getString(R.string.amount); 29 | String memo = appContext.getString(R.string.memo); 30 | 31 | JSONArray privateKeysJSON = new JSONArray(appContext.getString(R.string.private_keys)); 32 | 33 | for (int i = 0; i < privateKeysJSON.length(); i++) { 34 | testSubmitTransactionByMultipleKey(fromAccount, toAccount, amount, memo, privateKeysJSON.getString(i), nodeURL); 35 | Thread.sleep(1000); 36 | } 37 | } 38 | 39 | private void testSubmitTransactionByMultipleKey( 40 | String fromAccount, 41 | String toAccount, 42 | String amount, 43 | String memo, 44 | final String privateKey, 45 | String url) throws InterruptedException { 46 | 47 | final CountDownLatch signal = new CountDownLatch(1); 48 | 49 | new TransactionTask(new TransactionTask.TransactionTaskCallback() { 50 | @Override 51 | public void update(String updateContent) { 52 | 53 | } 54 | 55 | @Override 56 | public void finish(boolean success, String updateContent) { 57 | System.out.println("Finish Transaction " + updateContent + " - key " + privateKey); 58 | assertTrue(success); 59 | signal.countDown(); 60 | } 61 | }).execute(url, fromAccount, toAccount, privateKey, amount, memo); 62 | 63 | signal.await(5000, TimeUnit.MILLISECONDS); 64 | assertTrue(true); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/one/block/androidexampleapp/CheckBalanceTask.java: -------------------------------------------------------------------------------- 1 | package one.block.androidexampleapp; 2 | 3 | import android.os.AsyncTask; 4 | 5 | import org.json.JSONArray; 6 | import org.json.JSONException; 7 | 8 | import okhttp3.RequestBody; 9 | import one.block.eosiojava.error.rpcProvider.RpcProviderError; 10 | import one.block.eosiojava.models.rpcProvider.response.RPCResponseError; 11 | import one.block.eosiojavarpcprovider.error.EosioJavaRpcProviderInitializerError; 12 | import one.block.eosiojavarpcprovider.implementations.EosioJavaRpcProviderImpl; 13 | 14 | public class CheckBalanceTask extends AsyncTask { 15 | 16 | /** 17 | * Whether the network logs will be enabled for RPC provider 18 | */ 19 | private static final boolean ENABLE_NETWORK_LOG = true; 20 | 21 | private CheckBalanceTaskCallback callback; 22 | 23 | public CheckBalanceTask(CheckBalanceTaskCallback callback) { 24 | this.callback = callback; 25 | } 26 | 27 | @Override 28 | protected void onProgressUpdate(String... values) { 29 | super.onProgressUpdate(values); 30 | // Send back statuses to caller for progress update or finishing task with failure or success. 31 | if (values.length == 1) { 32 | // Updating case 33 | String message = values[0]; 34 | this.callback.update(message); 35 | } else if (values.length == 2) { 36 | // Failing case 37 | boolean isSuccess = Boolean.parseBoolean(values[0]); 38 | String message = values[1]; 39 | this.callback.finish(isSuccess, message, null); 40 | } else if (values.length == 3) { 41 | // Successful case 42 | boolean isSuccess = Boolean.parseBoolean(values[0]); 43 | String message = values[1]; 44 | String balance = values[2]; 45 | this.callback.finish(isSuccess, message, balance); 46 | } 47 | } 48 | 49 | @Override 50 | protected Void doInBackground(String... params) { 51 | String nodeUrl = params[0]; 52 | String fromAccount = params[1]; 53 | 54 | EosioJavaRpcProviderImpl rpcProvider; 55 | try { 56 | this.publishProgress("Checking Account Balance..."); 57 | rpcProvider = new EosioJavaRpcProviderImpl(nodeUrl, ENABLE_NETWORK_LOG); 58 | String getCurrentBalanceRequestJSON = "{\n" + 59 | "\t\"code\" : \"eosio.token\"\n" + 60 | "\t\"account\" : \"" + fromAccount + "\"\n" + 61 | "}"; 62 | 63 | RequestBody requestBody = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), getCurrentBalanceRequestJSON); 64 | String responseJSON = rpcProvider.getCurrencyBalance(requestBody); 65 | 66 | this.publishProgress("Account Balance Check Successful!"); 67 | 68 | JSONArray jsonArray = new JSONArray(responseJSON); 69 | if (jsonArray.length() == 0) { 70 | this.publishProgress(Boolean.toString(false), "Invalid Account!"); 71 | return null; 72 | } 73 | 74 | String accountBalance = jsonArray.getString(0); 75 | 76 | this.publishProgress(Boolean.toString(true), "Current Account Balance: " + accountBalance, accountBalance); 77 | } catch (EosioJavaRpcProviderInitializerError eosioJavaRpcProviderInitializerError) { 78 | // Happens if creating EosioJavaRpcProviderImpl unsuccessful 79 | eosioJavaRpcProviderInitializerError.printStackTrace(); 80 | 81 | this.publishProgress(Boolean.toString(false), eosioJavaRpcProviderInitializerError.asJsonString()); 82 | } catch (RpcProviderError rpcProviderError) { 83 | // Happens if calling getCurrentBalance unsuccessful 84 | rpcProviderError.printStackTrace(); 85 | 86 | // try to get response from backend if the process fail from backend 87 | RPCResponseError rpcResponseError = ErrorUtils.getBackendError(rpcProviderError); 88 | if (rpcResponseError != null) { 89 | String backendErrorMessage = ErrorUtils.getBackendErrorMessageFromResponse(rpcResponseError); 90 | this.publishProgress(Boolean.toString(false), backendErrorMessage); 91 | return null; 92 | } 93 | 94 | this.publishProgress(Boolean.toString(false), rpcProviderError.getMessage()); 95 | } catch (JSONException e) { 96 | // Happens if parsing JSON response unsuccessful 97 | e.printStackTrace(); 98 | this.publishProgress(Boolean.toString(false), e.getMessage()); 99 | } 100 | 101 | return null; 102 | } 103 | 104 | public interface CheckBalanceTaskCallback { 105 | void update(String updateContent); 106 | 107 | void finish(boolean success, String updateContent, String balance); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/one/block/androidexampleapp/ErrorUtils.java: -------------------------------------------------------------------------------- 1 | package one.block.androidexampleapp; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | import one.block.eosiojava.error.EosioError; 6 | import one.block.eosiojava.models.rpcProvider.response.Detail; 7 | import one.block.eosiojava.models.rpcProvider.response.RPCResponseError; 8 | import one.block.eosiojavarpcprovider.error.EosioJavaRpcProviderCallError; 9 | 10 | public class ErrorUtils { 11 | 12 | /** 13 | * Recursively look for a specific error inside causes loop of an EosioError 14 | * 15 | * @param errorClass - the error class to find 16 | * @param error - the error object to search 17 | * @param - the generic class which extends from EosioError 18 | * @return the error which class is specified by input. Return null if could not find the specific class. 19 | */ 20 | @Nullable 21 | public static T getErrorObject(Class errorClass, Exception error) { 22 | if (error.getClass() == errorClass) { 23 | return (T) error; 24 | } 25 | 26 | if (error.getCause() == null) { 27 | return null; 28 | } 29 | 30 | // Recursively look deeper 31 | return getErrorObject(errorClass, (Exception) error.getCause()); 32 | } 33 | 34 | /** 35 | * Recursively look for the error message of a specific error inside causes loop of an EosioError 36 | * 37 | * @param errorClass - the error class to get the message 38 | * @param error - the error object to search 39 | * @return the error message which class is specified by input. Return the root cause message if could not find the specific class. 40 | */ 41 | public static String getError(Class errorClass, EosioError error) { 42 | if (error.getClass() == errorClass || error.getCause() == null) { 43 | return error.getMessage(); 44 | } 45 | 46 | return getError(errorClass, (EosioError) error.getCause()); 47 | } 48 | 49 | /** 50 | * Get backend error class {@link RPCResponseError} if an backend error is available 51 | * 52 | * @param error the error class to get the backend error 53 | * @return {@link RPCResponseError} object. Return null if input error does not contain any backend error. 54 | */ 55 | @Nullable 56 | public static RPCResponseError getBackendError(EosioError error) { 57 | EosioJavaRpcProviderCallError rpcError = ErrorUtils.getErrorObject(EosioJavaRpcProviderCallError.class, error); 58 | if (rpcError != null) { 59 | return rpcError.getRpcResponseError(); 60 | } 61 | 62 | return null; 63 | } 64 | 65 | /** 66 | * Format and return a back end error message from a {@link RPCResponseError} object 67 | * 68 | * @param error the backend error 69 | * @return Formatted backend error message from input 70 | */ 71 | public static String getBackendErrorMessageFromResponse(@NonNull RPCResponseError error) { 72 | StringBuilder detail = new StringBuilder(); 73 | if (!error.getError().getDetails().isEmpty()) { 74 | for (Detail errorDetail : error.getError().getDetails()) { 75 | detail.append(errorDetail.getMessage()).append(" - "); 76 | } 77 | } 78 | 79 | return error.getMessage() + " - Code: " + error.getError().getCode() + " - What " + error.getError().getCode() + " - detail: " + detail.toString(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/one/block/androidexampleapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package one.block.androidexampleapp; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.os.Bundle; 6 | import android.text.Html; 7 | import android.text.method.ScrollingMovementMethod; 8 | import android.view.View; 9 | import android.widget.Button; 10 | import android.widget.EditText; 11 | import android.widget.TextView; 12 | 13 | import com.google.android.material.textfield.TextInputEditText; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | private TextView tvStatus; 21 | private Button btnTransfer; 22 | private Button btnCheckBalance; 23 | private TextView tvBalanceStatus; 24 | private List logs = new ArrayList<>(); 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_main); 30 | 31 | final String nodeUrl = this.getString(R.string.node_url); 32 | 33 | this.tvStatus = findViewById(R.id.tv_status); 34 | this.tvStatus.setMovementMethod(new ScrollingMovementMethod()); 35 | 36 | this.btnCheckBalance = findViewById(R.id.btn_check_balance); 37 | this.btnTransfer = findViewById(R.id.btn_transfer); 38 | this.tvBalanceStatus = findViewById(R.id.tv_balance); 39 | 40 | this.btnTransfer.setOnClickListener(new View.OnClickListener() { 41 | @Override 42 | public void onClick(View v) { 43 | executeTransaction(nodeUrl); 44 | } 45 | }); 46 | 47 | this.btnCheckBalance.setOnClickListener(new View.OnClickListener() { 48 | @Override 49 | public void onClick(View v) { 50 | executeCheckBalance(nodeUrl); 51 | } 52 | }); 53 | 54 | findViewById(R.id.btn_clear_log).setOnClickListener(new View.OnClickListener() { 55 | @Override 56 | public void onClick(View v) { 57 | logs.clear(); 58 | tvStatus.setText(""); 59 | } 60 | }); 61 | } 62 | 63 | private void update() { 64 | StringBuilder updateContentBuilder = new StringBuilder(); 65 | for (String log : this.logs) { 66 | updateContentBuilder.append(log); 67 | } 68 | 69 | this.tvStatus.setText(Html.fromHtml(updateContentBuilder.toString())); 70 | } 71 | 72 | private void executeTransaction(final String nodeUrl) { 73 | // Collecting necessary data to send transaction 74 | final String fromAccount = ((TextInputEditText) this.findViewById(R.id.edt_from_account)).getText().toString(); 75 | final String toAccount = ((TextInputEditText) this.findViewById(R.id.edt_to_account)).getText().toString(); 76 | final String privateKey = ((TextInputEditText) this.findViewById(R.id.edt_private_key)).getText().toString(); 77 | final String amount = ((TextInputEditText) this.findViewById(R.id.edt_amount)).getText().toString(); 78 | final String memo = ((TextInputEditText) this.findViewById(R.id.edt_memo)).getText().toString(); 79 | 80 | this.btnTransfer.setEnabled(false); 81 | new TransactionTask(new TransactionTask.TransactionTaskCallback() { 82 | @Override 83 | public void update(String updateContent) { 84 | logs.add("

" + updateContent + "

"); 85 | MainActivity.this.update(); 86 | } 87 | 88 | @Override 89 | public void finish(boolean success, String updateContent) { 90 | String message = success ? htmlSuccessFormat(updateContent) : htmlErrorFormat(updateContent); 91 | message += "

"; 92 | logs.add(message); 93 | MainActivity.this.update(); 94 | btnTransfer.setEnabled(true); 95 | 96 | if (success) { 97 | executeCheckBalance(nodeUrl); 98 | } 99 | } 100 | }).execute(nodeUrl, fromAccount, toAccount, privateKey, amount, memo); 101 | } 102 | 103 | private void executeCheckBalance(String nodeUrl) { 104 | // Collecting necessary data to check account balance 105 | final String account = ((EditText) this.findViewById(R.id.edt_from_account)).getText().toString(); 106 | 107 | this.btnCheckBalance.setEnabled(false); 108 | new CheckBalanceTask(new CheckBalanceTask.CheckBalanceTaskCallback() { 109 | @Override 110 | public void update(String updateContent) { 111 | logs.add("

" + updateContent + "

"); 112 | MainActivity.this.update(); 113 | } 114 | 115 | @Override 116 | public void finish(boolean success, String updateContent, String balance) { 117 | String message = success ? htmlSuccessFormat(updateContent) : htmlErrorFormat(updateContent); 118 | message += "

"; 119 | logs.add(message); 120 | MainActivity.this.update(); 121 | btnCheckBalance.setEnabled(true); 122 | 123 | if (success) { 124 | tvBalanceStatus.setVisibility(View.VISIBLE); 125 | tvBalanceStatus.setText(String.format("%s %s", getString(R.string.account_balance), balance)); 126 | } else { 127 | tvBalanceStatus.setVisibility(View.GONE); 128 | } 129 | } 130 | }).execute(nodeUrl, account); 131 | } 132 | 133 | private String htmlErrorFormat(String error) { 134 | return "

" + error + "

"; 135 | } 136 | 137 | private String htmlSuccessFormat(String msg) { 138 | return "

" + msg + "

"; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /app/src/main/java/one/block/androidexampleapp/TransactionTask.java: -------------------------------------------------------------------------------- 1 | package one.block.androidexampleapp; 2 | 3 | import android.os.AsyncTask; 4 | 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | import androidx.annotation.NonNull; 9 | import one.block.eosiojava.error.serializationProvider.SerializationProviderError; 10 | import one.block.eosiojava.error.session.TransactionPrepareError; 11 | import one.block.eosiojava.error.session.TransactionSignAndBroadCastError; 12 | import one.block.eosiojava.implementations.ABIProviderImpl; 13 | import one.block.eosiojava.interfaces.IABIProvider; 14 | import one.block.eosiojava.interfaces.IRPCProvider; 15 | import one.block.eosiojava.interfaces.ISerializationProvider; 16 | import one.block.eosiojava.interfaces.ISignatureProvider; 17 | import one.block.eosiojava.models.rpcProvider.Action; 18 | import one.block.eosiojava.models.rpcProvider.Authorization; 19 | import one.block.eosiojava.models.rpcProvider.Transaction; 20 | import one.block.eosiojava.models.rpcProvider.response.RPCResponseError; 21 | import one.block.eosiojava.models.rpcProvider.response.SendTransactionResponse; 22 | import one.block.eosiojava.session.TransactionProcessor; 23 | import one.block.eosiojava.session.TransactionSession; 24 | import one.block.eosiojavaabieosserializationprovider.AbiEosSerializationProviderImpl; 25 | import one.block.eosiojavarpcprovider.error.EosioJavaRpcProviderInitializerError; 26 | import one.block.eosiojavarpcprovider.implementations.EosioJavaRpcProviderImpl; 27 | import one.block.eosiosoftkeysignatureprovider.SoftKeySignatureProviderImpl; 28 | import one.block.eosiosoftkeysignatureprovider.error.ImportKeyError; 29 | 30 | /** 31 | * This class is an example about the most basic/easy way to use eosio-java to send a transaction. 32 | *

33 | * Basic steps: 34 | *

35 | * - Create serialization provider as an instant of {@link AbiEosSerializationProviderImpl} from [eosiojavaandroidabieosserializationprovider] library 36 | *

37 | * - Create RPC provider as an instant of {@link EosioJavaRpcProviderImpl} with an input string point to a node backend. 38 | *

39 | * - Create ABI provider as an instant of {@link ABIProviderImpl} with instants of Rpc provider and serialization provider. 40 | *

41 | * - Create Signature provider as an instant of {@link SoftKeySignatureProviderImpl} which is not recommended for production because of its simple key management. 42 | *

43 | * - Import an EOS private key which associate with sender's account which will be used to sign the transaction. 44 | *

45 | * - Create an instant of {@link TransactionSession} which is used for spawning/factory {@link TransactionProcessor} 46 | *

47 | * - Create an instant of {@link TransactionProcessor} from the instant of {@link TransactionSession} above by calling {@link TransactionSession#getTransactionProcessor()} or {@link TransactionSession#getTransactionProcessor(Transaction)} if desire to use a preset {@link Transaction} object. 48 | *

49 | * - Call {@link TransactionProcessor#prepare(List)} with a list of Actions which is desired to be sent to backend. The method will serialize the list of action to list of hex and keep them inside 50 | * the list of {@link Transaction#getActions()}. The transaction now is ready to be signed and broadcast. 51 | *

52 | * - Call {@link TransactionProcessor#signAndBroadcast()} to sign the transaction inside {@link TransactionProcessor} and broadcast it to backend. 53 | */ 54 | public class TransactionTask extends AsyncTask { 55 | 56 | /** 57 | * Whether the network logs will be enabled for RPC provider 58 | */ 59 | private static final boolean ENABLE_NETWORK_LOG = true; 60 | 61 | private TransactionTaskCallback callback; 62 | 63 | public TransactionTask(@NonNull TransactionTaskCallback callback) { 64 | this.callback = callback; 65 | } 66 | 67 | @Override 68 | protected void onProgressUpdate(String... values) { 69 | super.onProgressUpdate(values); 70 | if (values.length == 1) { 71 | String message = values[0]; 72 | this.callback.update(message); 73 | } else if (values.length == 2) { 74 | boolean isSuccess = Boolean.parseBoolean(values[0]); 75 | String message = values[1]; 76 | this.callback.finish(isSuccess, message); 77 | } 78 | } 79 | 80 | @Override 81 | protected Void doInBackground(String... params) { 82 | String nodeUrl = params[0]; 83 | String fromAccount = params[1]; 84 | String toAccount = params[2]; 85 | String privateKey = params[3]; 86 | String amount = params[4]; 87 | String memo = params[5]; 88 | 89 | this.publishProgress("Transferring " + amount + " to " + toAccount); 90 | 91 | // Creating serialization provider 92 | ISerializationProvider serializationProvider; 93 | try { 94 | serializationProvider = new AbiEosSerializationProviderImpl(); 95 | } catch (SerializationProviderError serializationProviderError) { 96 | serializationProviderError.printStackTrace(); 97 | return null; 98 | } 99 | 100 | // Creating RPC Provider 101 | IRPCProvider rpcProvider; 102 | try { 103 | rpcProvider = new EosioJavaRpcProviderImpl(nodeUrl, ENABLE_NETWORK_LOG); 104 | } catch (EosioJavaRpcProviderInitializerError eosioJavaRpcProviderInitializerError) { 105 | eosioJavaRpcProviderInitializerError.printStackTrace(); 106 | this.publishProgress(Boolean.toString(false), eosioJavaRpcProviderInitializerError.getMessage()); 107 | return null; 108 | } 109 | 110 | // Creating ABI provider 111 | IABIProvider abiProvider = new ABIProviderImpl(rpcProvider, serializationProvider); 112 | 113 | // Creating Signature provider 114 | ISignatureProvider signatureProvider = new SoftKeySignatureProviderImpl(); 115 | 116 | try { 117 | ((SoftKeySignatureProviderImpl) signatureProvider).importKey(privateKey); 118 | } catch (ImportKeyError importKeyError) { 119 | importKeyError.printStackTrace(); 120 | this.publishProgress(Boolean.toString(false), importKeyError.getMessage()); 121 | return null; 122 | } 123 | 124 | // Creating TransactionProcess 125 | TransactionSession session = new TransactionSession(serializationProvider, rpcProvider, abiProvider, signatureProvider); 126 | TransactionProcessor processor = session.getTransactionProcessor(); 127 | 128 | // Apply transaction data to Action's data 129 | String jsonData = "{\n" + 130 | "\"from\": \"" + fromAccount + "\",\n" + 131 | "\"to\": \"" + toAccount + "\",\n" + 132 | "\"quantity\": \"" + amount + "\",\n" + 133 | "\"memo\" : \"" + memo + "\"\n" + 134 | "}"; 135 | 136 | // Creating action with action's data, eosio.token contract and transfer action. 137 | Action action = new Action("eosio.token", "transfer", Collections.singletonList(new Authorization(fromAccount, "active")), jsonData); 138 | try { 139 | 140 | // Prepare transaction with above action. A transaction can be executed with multiple action. 141 | this.publishProgress("Preparing Transaction..."); 142 | processor.prepare(Collections.singletonList(action)); 143 | 144 | // Sign and broadcast the transaction. 145 | this.publishProgress("Signing and Broadcasting Transaction..."); 146 | SendTransactionResponse response = processor.signAndBroadcast(); 147 | 148 | this.publishProgress(Boolean.toString(true), "Finished! Your transaction id is: " + response.getTransactionId()); 149 | } catch (TransactionPrepareError transactionPrepareError) { 150 | // Happens if preparing transaction unsuccessful 151 | transactionPrepareError.printStackTrace(); 152 | this.publishProgress(Boolean.toString(false), transactionPrepareError.getLocalizedMessage()); 153 | } catch (TransactionSignAndBroadCastError transactionSignAndBroadCastError) { 154 | // Happens if Sign transaction or broadcast transaction unsuccessful. 155 | transactionSignAndBroadCastError.printStackTrace(); 156 | 157 | // try to get backend error if the error come from backend 158 | RPCResponseError rpcResponseError = ErrorUtils.getBackendError(transactionSignAndBroadCastError); 159 | if (rpcResponseError != null) { 160 | String backendErrorMessage = ErrorUtils.getBackendErrorMessageFromResponse(rpcResponseError); 161 | this.publishProgress(Boolean.toString(false), backendErrorMessage); 162 | return null; 163 | } 164 | 165 | this.publishProgress(Boolean.toString(false), transactionSignAndBroadCastError.getMessage()); 166 | } 167 | 168 | return null; 169 | } 170 | 171 | public interface TransactionTaskCallback { 172 | void update(String updateContent); 173 | 174 | void finish(boolean success, String updateContent); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 24 | 25 | 26 | 27 | 41 | 42 | 48 | 49 | 55 | 56 | 64 | 65 | 72 | 73 | 74 | 75 | 83 | 84 | 91 | 92 | 93 | 94 | 102 | 103 | 110 | 111 | 112 | 113 | 121 | 122 | 129 | 130 | 131 | 132 | 138 | 139 | 145 | 146 | 152 | 153 | 166 | 167 |