├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── et │ │ └── web3j │ │ └── web3jdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── et │ │ │ └── web3j │ │ │ ├── MainActivity.java │ │ │ └── util │ │ │ └── SecureRandomUtils.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 │ └── com │ └── et │ └── web3j │ └── web3jdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndrosEt/Web3j-demo/5ca7b1e91c60a128973d20ecdc7453790100d333/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web3j-demo 2 | (ETH Wallet)This is a demo for testing web3j and how to create HD-Wallet with Mnemonic((以太坊钱包)本demo使用web3j和bitcoinj创建包含助记词的钱包,支持助记词钱包导入). 3 | You can telk to me some functions that you want to achieve or anything you think about, Thinking is without boundaries.兄die 4 | 5 | 通用的以太坊基于bip44协议的助记词路径(有三种取回路径,默认使用第一种) 6 | 7 | * "m/44'/60'/0'/0/0"; 8 | * "m/44'/60'/0'/0"; 9 | * "m/44'/60'/1'/0/0"; 10 | 11 | demo: 12 | 13 | https://play.google.com/store/apps/details?id=com.et.ewallet 14 | 15 | https://www.coolapk.com/apk/199976 16 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.et.web3j.web3jdemo" 7 | minSdkVersion 15 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | multiDexEnabled true 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | implementation 'com.android.support:appcompat-v7:28.0.0-alpha3' 25 | implementation 'com.android.support.constraint:constraint-layout:1.1.2' 26 | testImplementation 'junit:junit:4.12' 27 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 28 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 29 | 30 | implementation'org.bitcoinj:bitcoinj-core:0.14.3' 31 | implementation 'org.web3j:crypto:3.3.1-android' 32 | implementation 'com.orhanobut:logger:2.1.1' 33 | implementation 'com.github.zzz40500:android-shapeLoadingView:1.0.3.2' 34 | } 35 | -------------------------------------------------------------------------------- /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/com/et/web3j/web3jdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.et.web3j.web3jdemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.et.web3j.web3jdemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/et/web3j/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.et.web3j; 2 | 3 | import android.Manifest; 4 | import android.content.pm.PackageManager; 5 | import android.os.Build; 6 | import android.os.Handler; 7 | import android.support.annotation.RequiresApi; 8 | import android.support.v4.content.ContextCompat; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.os.Bundle; 11 | import android.view.View; 12 | import android.widget.Button; 13 | import android.widget.EditText; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | 17 | import com.et.web3j.util.SecureRandomUtils; 18 | import com.et.web3j.web3jdemo.R; 19 | import com.mingle.widget.LoadingView; 20 | import com.orhanobut.logger.AndroidLogAdapter; 21 | import com.orhanobut.logger.Logger; 22 | 23 | import org.bitcoinj.crypto.ChildNumber; 24 | import org.bitcoinj.crypto.DeterministicKey; 25 | import org.bitcoinj.crypto.HDKeyDerivation; 26 | import org.bitcoinj.wallet.DeterministicSeed; 27 | import org.web3j.crypto.CipherException; 28 | import org.web3j.crypto.Credentials; 29 | import org.web3j.crypto.ECKeyPair; 30 | import org.web3j.crypto.Keys; 31 | import org.web3j.crypto.Wallet; 32 | import org.web3j.crypto.WalletFile; 33 | import org.web3j.crypto.WalletUtils; 34 | import org.web3j.utils.Numeric; 35 | 36 | import java.security.SecureRandom; 37 | import java.util.Arrays; 38 | import java.util.List; 39 | 40 | public class MainActivity extends AppCompatActivity { 41 | 42 | private Button btCreate, btImport, btCleanData; 43 | private EditText etMnemonic; 44 | private TextView tvResult; 45 | private LoadingView loadingView; 46 | 47 | private final String password = "test1011"; 48 | /** 49 | * 通用的以太坊基于bip44协议的助记词路径 (imtoken jaxx Metamask myetherwallet) 50 | */ 51 | public static String ETH_JAXX_TYPE = "m/44'/60'/0'/0/0"; 52 | public static String ETH_LEDGER_TYPE = "m/44'/60'/0'/0"; 53 | public static String ETH_CUSTOM_TYPE = "m/44'/60'/1'/0/0"; 54 | 55 | /** 56 | * 随机 57 | */ 58 | private static final SecureRandom secureRandom = SecureRandomUtils.secureRandom(); 59 | 60 | public static final int REQUEST_CODE_EXTERNAL = 1; 61 | 62 | @RequiresApi(api = Build.VERSION_CODES.M) 63 | @Override 64 | protected void onCreate(Bundle savedInstanceState) { 65 | super.onCreate(savedInstanceState); 66 | setContentView(R.layout.activity_main); 67 | initView(); 68 | initPermission(); 69 | } 70 | 71 | /** 72 | * check the permission 73 | */ 74 | @RequiresApi(api = Build.VERSION_CODES.M) 75 | private void initPermission() { 76 | Logger.addLogAdapter(new AndroidLogAdapter() { 77 | @Override 78 | public boolean isLoggable(int priority, String tag) { 79 | return true; 80 | } 81 | }); 82 | 83 | if(!(ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) { 84 | requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_EXTERNAL); 85 | } 86 | } 87 | 88 | /** 89 | * get the result from user action 90 | * @param requestCode 91 | * @param permissions 92 | * @param grantResults 93 | */ 94 | @Override 95 | public void onRequestPermissionsResult(int requestCode, @android.support.annotation.NonNull String[] permissions, @android.support.annotation.NonNull int[] grantResults) { 96 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 97 | switch (requestCode) { 98 | case REQUEST_CODE_EXTERNAL: 99 | if (!(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // 权限申请成功 100 | Toast.makeText(MainActivity.this, "Write storage permissions are required", Toast.LENGTH_SHORT).show(); 101 | new Handler().postDelayed(new Runnable() { 102 | @Override 103 | public void run() { 104 | finish(); 105 | } 106 | }, 1000); 107 | } 108 | break; 109 | } 110 | } 111 | 112 | private void initView() { 113 | btCreate = findViewById(R.id.bt_create); 114 | btImport = findViewById(R.id.bt_import); 115 | etMnemonic = findViewById(R.id.et_mnemonic); 116 | tvResult = findViewById(R.id.tv_result); 117 | btCleanData = findViewById(R.id.bt_clean_data); 118 | loadingView = findViewById(R.id.loadView); 119 | 120 | btCreate.setOnClickListener(new View.OnClickListener() { 121 | @Override 122 | public void onClick(View view) { 123 | new Thread(new Runnable() { 124 | @Override 125 | public void run() { 126 | String[] pathArray = ETH_JAXX_TYPE.split("/"); 127 | String passphrase = ""; 128 | long creationTimeSeconds = System.currentTimeMillis() / 1000; 129 | DeterministicSeed ds = new DeterministicSeed(secureRandom, 128, passphrase, creationTimeSeconds); 130 | createWallet(ds, pathArray); 131 | } 132 | }).start(); 133 | 134 | } 135 | }); 136 | 137 | btImport.setOnClickListener(new View.OnClickListener() { 138 | @Override 139 | public void onClick(View view) { 140 | final String data = etMnemonic.getText().toString().trim(); 141 | if (!data.equals("")) { 142 | new Thread(new Runnable() { 143 | @Override 144 | public void run() { 145 | importWallet(data); 146 | } 147 | }).start(); 148 | etMnemonic.setText(""); 149 | } else { 150 | Toast.makeText(MainActivity.this, "pls input mnemonic", Toast.LENGTH_SHORT).show(); 151 | } 152 | } 153 | }); 154 | 155 | btCleanData.setOnClickListener(new View.OnClickListener() { 156 | @Override 157 | public void onClick(View view) { 158 | tvResult.setText(""); 159 | } 160 | }); 161 | } 162 | 163 | /** 164 | * import mnemonic wallet 165 | * @param mnemonic 166 | */ 167 | private void importWallet(String mnemonic) { 168 | String[] pathArray = ETH_JAXX_TYPE.split("/"); 169 | if (pathArray.length <= 1) { 170 | //内容不对 171 | return ; 172 | } 173 | String passphrase = ""; 174 | long creationTimeSeconds = System.currentTimeMillis() / 1000; 175 | DeterministicSeed ds = new DeterministicSeed(Arrays.asList(mnemonic.split(" ")), null, passphrase, creationTimeSeconds); 176 | createWallet(ds, pathArray); 177 | } 178 | 179 | /** 180 | * create wallet 181 | */ 182 | private void createWallet(DeterministicSeed ds, String[] pathArray) { 183 | runOnUiThread(new Runnable() { 184 | @Override 185 | public void run() { 186 | tvResult.setVisibility(View.GONE); 187 | loadingView.setVisibility(View.VISIBLE); 188 | } 189 | }); 190 | 191 | byte[] seedBytes = ds.getSeedBytes(); 192 | List mnemonicCode = ds.getMnemonicCode(); 193 | 194 | DeterministicKey masterPrivateKey = HDKeyDerivation.createMasterPrivateKey(seedBytes); 195 | 196 | for (int i = 1; i < pathArray.length; i++) { 197 | ChildNumber childNumber; 198 | if (pathArray[i].endsWith("'")) { 199 | int number = Integer.parseInt(pathArray[i].substring(0, pathArray[i].length() - 1)); 200 | childNumber = new ChildNumber(number, true); 201 | } else { 202 | int number = Integer.parseInt(pathArray[i]); 203 | childNumber = new ChildNumber(number, false); 204 | } 205 | 206 | masterPrivateKey = HDKeyDerivation.deriveChildKey(masterPrivateKey, childNumber); 207 | } 208 | 209 | ECKeyPair ecKeyPair = ECKeyPair.create(masterPrivateKey.getPrivKeyBytes()); 210 | WalletFile walletFile; 211 | try { 212 | walletFile = Wallet.create(password, ecKeyPair, 1024, 1); 213 | 214 | String address = Numeric.prependHexPrefix(Keys.getAddress(ecKeyPair)); 215 | String mnemonic = convertMnemonicList(mnemonicCode); 216 | Logger.d("Et Mnemonic : " + mnemonic); 217 | Logger.d("Et Address : " + address); 218 | final StringBuilder builder = new StringBuilder(); 219 | builder.append("Et Mnemonic : " + mnemonic + "\n"); 220 | builder.append("Et Address : " + address + "\n" + "\n"); 221 | runOnUiThread(new Runnable() { 222 | @Override 223 | public void run() { 224 | tvResult.setVisibility(View.VISIBLE); 225 | loadingView.setVisibility(View.GONE); 226 | tvResult.setText(builder.toString()); 227 | } 228 | }); 229 | 230 | } catch (CipherException e) { 231 | e.printStackTrace(); 232 | } 233 | } 234 | 235 | /** 236 | * covert mnemonic string to List 237 | * @param mnemonics 238 | * @return 239 | */ 240 | private static String convertMnemonicList(List mnemonics) { 241 | StringBuilder sb = new StringBuilder(); 242 | for (String mnemonic : mnemonics 243 | ) { 244 | sb.append(mnemonic); 245 | sb.append(" "); 246 | } 247 | return sb.toString(); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /app/src/main/java/com/et/web3j/util/SecureRandomUtils.java: -------------------------------------------------------------------------------- 1 | package com.et.web3j.util; 2 | 3 | import org.web3j.crypto.LinuxSecureRandom; 4 | 5 | import java.security.SecureRandom; 6 | 7 | /** 8 | * Created by Et on 2018/7/23 09:16 9 | * email:x313371005@126.com 10 | */ 11 | public class SecureRandomUtils { 12 | 13 | private static final SecureRandom SECURE_RANDOM; 14 | 15 | static { 16 | if (isAndroidRuntime()) { 17 | new LinuxSecureRandom(); 18 | } 19 | SECURE_RANDOM = new SecureRandom(); 20 | } 21 | 22 | public static SecureRandom secureRandom() { 23 | return SECURE_RANDOM; 24 | } 25 | 26 | // Taken from BitcoinJ implementation 27 | // https://github.com/bitcoinj/bitcoinj/blob/3cb1f6c6c589f84fe6e1fb56bf26d94cccc85429/core/src/main/java/org/bitcoinj/core/Utils.java#L573 28 | private static int isAndroid = -1; 29 | 30 | static boolean isAndroidRuntime() { 31 | if (isAndroid == -1) { 32 | final String runtime = System.getProperty("java.runtime.name"); 33 | isAndroid = (runtime != null && runtime.equals("Android Runtime")) ? 1 : 0; 34 | } 35 | return isAndroid == 1; 36 | } 37 | 38 | private SecureRandomUtils() { } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /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 | 10 | 11 | 12 |