Wallet development experience sharing: ETH

[TOC]

Preparation before development

To do a good job, he must sharpen his tools

Developed all the way over, accumulated some wallet development sharp weapon and website, share with everyone. These things are known to anyone who has developed in the industry, just as a reference for people outside the industry who are planning to get into the industry.

  • The best ETH wallet -MetaMask

    Download: MetaMask (Google Plugin)

    This is an Ethereum browser plug-in that allows you to easily view and manipulate the balance of Ethereum and ERC20 tokens, as well as deploy contracts with a contract IDE such as Remix. It supports custom tokens, test networks, formal networks, and custom network nodes. All in all, this is a very handy wallet to use.

  • The most official blockchain browser — EtherScan

    Website: Ethereum’s official blockchain browser

    Introduction: This is the most official blockchain browser of Ethereum, for developers, it is not only simple query block transactions, it has more functions for programmers to develop. It provides a number of apis and widgets, it is the parent domain of all the test networks, it can easily switch to see all the blocks and transactions on all the test networks, and it helps you to publish contracts when you deploy them, so it is an indispensable site for developers.

  • Sites to obtain test coins — Rinkeby, Ropsten

    Website: Rinkeby, Ropsten

    Summary: Ethereum has many shared test networks. This blog post introduces the differences between each network and its blockchain browser. The main blockchain browsers used by developers are Rinkeby and Ropsten, and the above two sites are the tap sites for these two test coins. Obtain Rinkeby test coins and Ropsten test coins.

  • Free third-party node access – Wang Station

    Address: infura

    Summary: This is an indispensable site for ETH Wallet development. Of course, there may be other third-party nodes that are free to users, but I always use this site. The function of this website is that we can develop ETH normally without setting up ETH node. We just need to register an account with our finger and create our project, and we can get a free access to ETH node, and it also includes all the popular test networks. I call it king because its website icon resembles a king.

  • The most convenient Ethereum IDE- Remix

    Address: want to

    Summary: Contract development and deployment may be an essential part of ETH Wallet development. Why do I say this? That is because in the development of wallet, it is always necessary to connect with various ERC20 tokens. Although we can obtain ETH test coins, it is difficult (or impossible) to obtain the test coins of other tokens, and the token code based on ERC20 protocol is universal, so when accessing the token wallet, We tend to consider deploying a contract for the ERC20 protocol on our test network and minting our own coins to facilitate further development, and deploying the contract with Remix and MetaMask is a matter of several steps. The process for deploying the contract can be referenced in this tutorial.

ETH wallet code reference

Real knowledge lies in experience

Generate wallet address, key and mnemonic/recover wallet address, key and key by mnemonic

  • Import dependence

    <dependency>
    			<groupId>org.bitcoinj</groupId>
    			<artifactId>bitcoinj-core</artifactId>
    			<version>0.14.7</version>
    </dependency>
    
    <dependency>
    			<groupId>org.web3j</groupId>
    			<artifactId>core</artifactId>
    			<version>4.5.5</version>
    </dependency>
    Copy the code
  • Initialize web3j

    private final static Web3j web3j = Web3j.build(new HttpService("Https://mainnet.infura.io/v3/ yourself from infura application id"));
    Copy the code
  • Reference code

    	public static Map<String, Object> ethWalletGenerate(String mnemonic, String mnemonicPath, String passWord) {
    
    		try {
    			DeterministicSeed deterministicSeed = null;
    			List<String> mnemonicArray = null;
    
    			if (null == mnemonic || 0 == mnemonic.length()) {
    				deterministicSeed = new DeterministicSeed(new SecureRandom(), 128."", System.currentTimeMillis() / 1000);
    				mnemonicArray = deterministicSeed.getMnemonicCode();/ / mnemonic word
    			} else {
    				deterministicSeed = new DeterministicSeed(mnemonic, null."", System.currentTimeMillis() / 1000);
    			}
    
    			byte[] seedBytes = deterministicSeed.getSeedBytes();/ / seeds
    			if (null == seedBytes) {
    				logger.error("Failed to generate wallet");
    				return null;
    			}
    
    			// Seed object
    			DeterministicKey deterministicKey = HDKeyDerivation.createMasterPrivateKey(seedBytes);
    
    			String[] pathArray = mnemonicPath.split("/");// Mnemonic path
    			for (int i = 1; i < pathArray.length; i++) {
    				ChildNumber childNumber;
    				if (pathArray[i].endsWith("'")) {
    					int number = Integer.parseInt(pathArray[i].substring(0, pathArray[i].length() - 1));
    					childNumber = new ChildNumber(number, true);
    				} else {
    					int number = Integer.parseInt(pathArray[i]);
    					childNumber = new ChildNumber(number, false);
    				}
    				deterministicKey = HDKeyDerivation.deriveChildKey(deterministicKey, childNumber);
    			}
    
    			ECKeyPair eCKeyPair = ECKeyPair.create(deterministicKey.getPrivKeyBytes());
    			WalletFile walletFile = Wallet.createStandard(passWord, eCKeyPair);
    			if (null == mnemonic || 0 == mnemonic.length()) {
    				StringBuilder mnemonicCode = new StringBuilder();
    				for (int i = 0; i < mnemonicArray.size(); i++) {
    					mnemonicCode.append(mnemonicArray.get(i)).append("");
    				}
    				return new HashMap<String, Object>() {
    					private static final long serialVersionUID = -4960785990664709623L;
    					{
    						put("walletFile", walletFile);
    						put("eCKeyPair", eCKeyPair);
    						put("mnemonic", mnemonicCode.substring(0, mnemonicCode.length() - 1)); }}; }else {
    				return new HashMap<String, Object>() {
    					private static final long serialVersionUID = -947886783923530545L;
    					{
    						put("walletFile", walletFile);
    						put("eCKeyPair", eCKeyPair); }}; }}catch (CipherException e) {
    			return null;
    		} catch (UnreadableWalletException e) {
    			return null; }}Copy the code

    For an explanation of the mnemonicPath, see this article: about wallet mnemonics. The erC20 token’s wallet address and ETH wallet address are common, so this code can be used to generate ETH wallet address, also can be used to generate ERC20 wallet address.

  • The test code

    	/** * Generate wallet address, public key, mnemonic */
    	@Test
    	public void testGenerateEthWallet(a){
    		Map<String, Object> wallet = AddrUtil.ethWalletGenerate(null, ETH_MNEMONI_PATH, "123456");
    		WalletFile walletFile = (WalletFile) wallet.get("walletFile");
    		String address = walletFile.getAddress();
    		ECKeyPair eCKeyPair = (ECKeyPair) wallet.get("eCKeyPair");
    		String privateKey = eCKeyPair.getPrivateKey().toString(16);
    		String publicKey = eCKeyPair.getPublicKey().toString(16);
    		String mnemonic = (String) wallet.get("mnemonic");
    		logger.warn("address: {}, privateKey: {}, publicKey: {}, mnemonic: {}", address, privateKey, publicKey, mnemonic);
    	}
    
    	/** * Recover wallet address, public/private key */ by mnemonic
    	@Test
    	public void testGenerateEthWalletByMnemonic(a){
    		Map<String, Object> wallet = AddrUtil.ethWalletGenerate("clown cat senior keep problem engine degree modify ritual machine syrup company", ETH_MNEMONI_PATH, "123456");
    		WalletFile walletFile = (WalletFile) wallet.get("walletFile");
    		String address = walletFile.getAddress();
    		ECKeyPair eCKeyPair = (ECKeyPair) wallet.get("eCKeyPair");
    		String privateKey = eCKeyPair.getPrivateKey().toString(16);
    		String publicKey = eCKeyPair.getPublicKey().toString(16);
    		String mnemonic = (String) wallet.get("mnemonic");
    		logger.warn("address: {}, privateKey: {}, publicKey: {}, mnemonic: {}", address, privateKey, publicKey, mnemonic);
    	}
    Copy the code

Further, we may want to be able to deduce all the wallet addresses and keys of the exchange from a unique key or mnemonic, please refer to this set of codes:

  • Reference code

        /** * generate the corresponding subaccount ** with the mnemonic and id@paramMnemonic *@paramId Indicates the id * of the derived child@returnSubaccount key */
        private static DeterministicKey generateKeyFromMnemonicAndUid(String mnemonic, int id) {
            byte[] seed = MnemonicUtils.generateSeed(mnemonic, "");
    
            DeterministicKey rootKey = HDKeyDerivation.createMasterPrivateKey(seed);
            DeterministicHierarchy hierarchy = new DeterministicHierarchy(rootKey);
    
            return hierarchy.deriveChild(BIP44_ETH_ACCOUNT_ZERO_PATH, false.true.new ChildNumber(id, false));
        }
    
        /** * Generate address **@paramId User ID *@returnAddress * /
        public static String getEthAddress(String mnemonic, int id) {
            DeterministicKey deterministicKey = generateKeyFromMnemonicAndUid(mnemonic, id);
            ECKeyPair ecKeyPair = ECKeyPair.create(deterministicKey.getPrivKey());
            return Keys.getAddress(ecKeyPair);
        }
    
        /** * generate private key **@paramId User ID *@returnThe private key * /
        public static BigInteger getPrivateKey(String mnemonic, int id) {
            return generateKeyFromMnemonicAndUid(mnemonic, id).getPrivKey();
        }
    Copy the code
  • The test code

    	/** * Generate wallet address and private key */ from the mnemonic and user ID
    	@Test
    	public void testGenerateEthChildWallet(a){
    		String ethAddress = EthUtil.getEthAddress("clown cat senior keep problem engine degree modify ritual machine syrup company".1);
    		BigInteger privateKey = EthUtil.getPrivateKey("clown cat senior keep problem engine degree modify ritual machine syrup company".1);
    		logger.warn("address: {}, privateKey: {}", ethAddress, privateKey);
    	}
    Copy the code

Get the balance/get the coin balance

  • Reference code

        /** * Get eth balance **@paramAddress Address of the incoming query *@return* String balance@throws IOException
         */
        public static String getEthBalance(String address) {
            EthGetBalance ethGetBlance = null;
            try {
                ethGetBlance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
            } catch (IOException e) {
                logger.error([Failed to obtain ETH balance] error message: {}", e.getMessage());
            }
            // Format conversion WEI --> ETHER
            String balance = Convert.fromWei(new BigDecimal(ethGetBlance.getBalance()), Convert.Unit.ETHER).toPlainString();
            return balance;
        }
    Copy the code
  • The test code

    	/** * Get ETH balance */
    	@Test
    	public void testGetETHBalance(a){
    		String balance = EthUtil.getEthBalance("0x09f20ff67db2c5fabeb9a2c8dd5f6b4afab7887b");
    		logger.warn("balance: {}", balance);
    	}
    Copy the code
  • Reference code

        /** * get account token balance **@paramAccount Account address *@paramCoinAddress Contract address *@returnToken balance (unit: the smallest token unit) *@throws IOException
         */
        public static String getTokenBalance(String account, String coinAddress) {
            Function balanceOf = new Function("balanceOf",
                    Arrays.<Type>asList(neworg.web3j.abi.datatypes.Address(account)), Arrays.<TypeReference<? >>asList(new TypeReference<Uint256>() {
                    }));
    
            if (coinAddress == null) {
                return null;
            }
            String value = null;
            try {
                value = web3j.ethCall(Transaction.createEthCallTransaction(account, coinAddress, FunctionEncoder.encode(balanceOf)), DefaultBlockParameterName.PENDING).send().getValue();
            } catch (IOException e) {
                logger.error("[failed to get contract token balance] error message: {}", e.getMessage());
                return null;
            }
            int decimal = getTokenDecimal(coinAddress);
            BigDecimal balance = new BigDecimal(new BigInteger(value.substring(2), 16).toString(10)).divide(BigDecimal.valueOf(Math.pow(10, decimal)));
            return balance.toPlainString();
        }
    Copy the code
  • The test code

    	/** * get the balance of tokens */
    	@Test
    	public void testGetTokenBalance(a){
    		String usdtBalance = EthUtil.getTokenBalance("0x09f20ff67db2c5fabeb9a2c8dd5f6b4afab7887b"."0xdac17f958d2ee523a2206206994597c13d831ec7");
    		logger.warn("usdtBalance: {}", usdtBalance);
    	}
    Copy the code

    ETH address is divided into two kinds, one is the common user address, the other is the contract address, all token types of transfer is initiated to the contract address transfer, in the input of the actual account information (address and quantity), all kinds of token contract address can be consulted the most official ethereum block browser. The code in the above reference code that gets the precision of the substitution can continue to refer to the code below.

The name, precision and symbol of the replaced coin

  • Reference code

        /** * Query token */
        public static String getTokenSymbol(String contractAddress) {
            String methodName = "symbol";
            List<Type> inputParameters = newArrayList<>(); List<TypeReference<? >> outputParameters =new ArrayList<>();
    
            TypeReference<Utf8String> typeReference = new TypeReference<Utf8String>() {
            };
            outputParameters.add(typeReference);
    
            Function function = new Function(methodName, inputParameters, outputParameters);
    
            String data = FunctionEncoder.encode(function);
            Transaction transaction = Transaction.createEthCallTransaction("0x0000000000000000000000000000000000000000", contractAddress, data);
    
            EthCall ethCall = null;
            try {
                ethCall = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).sendAsync().get();
            } catch (InterruptedException e) {
                logger.error("Failed to obtain replacement token");
                e.printStackTrace();
            } catch (ExecutionException e) {
                logger.error("Failed to obtain replacement token");
                e.printStackTrace();
            }
            List<Type> results = FunctionReturnDecoder.decode(ethCall.getValue(), function.getOutputParameters());
            if (null == results || 0 == results.size()) {
                return "";
            }
            return results.get(0).getValue().toString();
        }
    
        /** * query the token name */
        public static String getTokenName(String contractAddr) {
            String methodName = "name";
            List<Type> inputParameters = newArrayList<>(); List<TypeReference<? >> outputParameters =new ArrayList<>();
    
            TypeReference<Utf8String> typeReference = new TypeReference<Utf8String>() {
            };
            outputParameters.add(typeReference);
    
            Function function = new Function(methodName, inputParameters, outputParameters);
    
            String data = FunctionEncoder.encode(function);
            Transaction transaction = Transaction.createEthCallTransaction("0x0000000000000000000000000000000000000000", contractAddr, data);
    
            EthCall ethCall = null;
            try {
                ethCall = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).sendAsync().get();
            } catch (InterruptedException e) {
                logger.error("Failed to obtain substitution coin name");
                e.printStackTrace();
            } catch (ExecutionException e) {
                logger.error("Failed to obtain substitution coin name");
                e.printStackTrace();
            }
            List<Type> results = FunctionReturnDecoder.decode(ethCall.getValue(), function.getOutputParameters());
            if (null == results || results.size() <= 0) {
                return "";
            }
            return results.get(0).getValue().toString();
        }
    
        /** * Query token precision */
        public static int getTokenDecimal(String contractAddr) {
            String methodName = "decimals";
            List<Type> inputParameters = newArrayList<>(); List<TypeReference<? >> outputParameters =new ArrayList<>();
    
            TypeReference<Uint8> typeReference = new TypeReference<Uint8>() {
            };
            outputParameters.add(typeReference);
    
            Function function = new Function(methodName, inputParameters, outputParameters);
    
            String data = FunctionEncoder.encode(function);
            Transaction transaction = Transaction.createEthCallTransaction("0x0000000000000000000000000000000000000000", contractAddr, data);
    
            EthCall ethCall = null;
            try {
                ethCall = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).sendAsync().get();
            } catch (InterruptedException e) {
                logger.error("Accuracy of replacement coin failed");
                e.printStackTrace();
            } catch (ExecutionException e) {
                logger.error("Accuracy of replacement coin failed");
                e.printStackTrace();
            }
            List<Type> results = FunctionReturnDecoder.decode(ethCall.getValue(), function.getOutputParameters());
            if (null == results || 0 == results.size()) {
                return 0;
            }
            return Integer.parseInt(results.get(0).getValue().toString());
        }
    Copy the code
  • The test code

    	/** * replaces coin name, symbol and precision */
    	@Test
    	public void testGetTokenInfo(a){
    		String usdtContractAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7";
    		String tokenName = EthUtil.getTokenName(usdtContractAddress);
    		String tokenSymbol = EthUtil.getTokenSymbol(usdtContractAddress);
    		int tokenDecimal = EthUtil.getTokenDecimal(usdtContractAddress);
    		logger.warn("name: {}, symbol: {}, decimal: {}", tokenName, tokenSymbol, tokenDecimal);
    	}
    Copy the code

Get the trade

  • Reference code

        /** * Get block transactions based on block height *@paramHeight Block height *@return* /
        public static List<Transaction> getTxByHeight(BigInteger height) {
            List<Transaction> transactions = new ArrayList<>();
            try {
                EthBlock.Block block = web3j.ethGetBlockByNumber(DefaultBlockParameter.valueOf(height), false).send().getBlock();
                for (EthBlock.TransactionResult transactionResult : block.getTransactions()) {
                    Transaction transaction = web3j.ethGetTransactionByHash((String) transactionResult.get()).send().getTransaction().get();
                    transactions.add(transaction);
                }
                logger.info(Block hash: {}, block height: {}", block.getHash(), block.getNumber());
            } catch (IOException e) {
                logger.error("[failed to get transaction data] error message: {}", e.getMessage());
                return null;
            }
            return transactions;
        }
    
    		/** * Get transaction information from txID *@paramTxid transaction hash *@return* /
        public static Transaction getTxByTxid(String txid) {
            Transaction transaction = null;
            try {
                transaction = web3j.ethGetTransactionByHash(txid).send().getTransaction().orElse(null);
                logger.info("[Transaction information obtained successfully] {} : {}", txid, new Gson().toJson(transaction));
            } catch (IOException e) {
                logger.info(Transaction hash: {}, error message: {}", txid, e.getMessage());
                return null;
            }
            return transaction;
        }
    
        /** * Parse token transactions *@paramTransaction Transaction object *@return* /
        public static Map<String, Object> getTokenTxInfo(Transaction transaction){
            Map<String, Object> result = new HashMap<>();
            String input = transaction.getInput();
            if(! Erc20Util.isTransferFunc(input)) {return null;
            }
            result.put("to", Erc20Util.getToAddress(input));
            result.put("amount", Erc20Util.getTransferValue(input).divide(BigDecimal.valueOf(Math.pow(10, getTokenDecimal(transaction.getTo())))));
            result.put("txid", transaction.getHash());
            result.put("from", transaction.getFrom());
            result.put("height", transaction.getBlockNumber());
            result.put("txFee", Convert.fromWei(transaction.getGasPrice().multiply(transaction.getGas()).toString(10), Convert.Unit.ETHER));
            result.put("gas", transaction.getGas());
            result.put("gasPrice", transaction.getGasPrice());
            return result;
        }
    
        /** * Parse ETH transaction *@paramTransaction Transaction object *@return* /
        public static Map<String, Object> getEthTxInfo(Transaction transaction){
            Map<String, Object> result = new HashMap<>();
            result.put("to", transaction.getTo());
            result.put("amount", Convert.fromWei(transaction.getValue().toString(10), Convert.Unit.ETHER));
            result.put("txid", transaction.getHash());
            result.put("from", transaction.getFrom());
            result.put("height", transaction.getBlockNumber());
            result.put("txFee", Convert.fromWei(transaction.getGasPrice().multiply(transaction.getGas()).toString(10), Convert.Unit.ETHER));
            result.put("gas", transaction.getGas());
            result.put("gasPrice", transaction.getGasPrice());
            return result;
        }
    Copy the code
  • The test code

    	/** * Get ETH/ token transaction information according to txID */
    	@Test
    	public void testGetTransactionByTxid(a){
    		Transaction ethTx = EthUtil.getTxByTxid("0xd05798408be19ec0adc5e0a7397b4e9d294b8e136eacc1eb606be45533eb97f1");
    		Map<String, Object> ethTxInfo = EthUtil.getEthTxInfo(ethTx);
    
    		Transaction usdtTx = EthUtil.getTxByTxid("0xd5443fad2feafd309f28d86d39af2e3f112b1ca1b8cdce8a2b6b9cdcdef5ad59");
    		Map<String, Object> usdtTxInfo = EthUtil.getTokenTxInfo(usdtTx);
    
    		logger.warn("txInfo: {}, usdtTxInfo: {}".new Gson().toJson(ethTxInfo), new Gson().toJson(usdtTxInfo));
    	}
    
    	/** * Get transactions based on block height */
    	@Test
    	public void testGetTransactionByBlockHeight(a){
    		List<Transaction> transactions = EthUtil.getTxByHeight(new BigInteger("9159698"));
    		logger.warn("txCount: {}", transactions.size());
    	}
    Copy the code

ETH/ token offline signature transfer

  • Reference code

        /** * Send eth offline transaction **@paramFrom eth Holding address *@paramTo Send destination address *@paramAmount Amount (unit: ETH) *@paramThe credentials key object *@returnHash * /
        public static String sendEthTx(String from, String to, BigInteger gasLimit, BigInteger gasPrice, BigDecimal amount, Credentials credentials) {
    
            try{ BigInteger nonce = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.PENDING).send().getTransactionCount();  BigInteger amountWei = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger(); RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, to, amountWei,"");
    
                byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
    
                return web3j.ethSendRawTransaction(Numeric.toHexString(signMessage)).sendAsync().get().getTransactionHash();
            }catch (Exception e) {
                logger.error([ETH offline transfer failed] Error message: {}", e.getMessage());
                return null; }}/** * Send tokens for offline transactions **@paramFrom token holding address *@paramTo token target address *@paramValue Amount (the smallest unit of token) *@paramCoinAddress Token contract address *@paramThe credentials key object *@returnHash * /
        public static String sendTokenTx(String from, String to, BigInteger gasLimit, BigInteger gasPrice, BigInteger value, String coinAddress, Credentials credentials) {
    
            try{ BigInteger nonce = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.PENDING).send().getTransactionCount();  Function function =new Function(
                        "transfer",
                        Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(to),
                                neworg.web3j.abi.datatypes.generated.Uint256(value)), Collections.<TypeReference<? >>emptyList()); String data = FunctionEncoder.encode(function); RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, coinAddress, data);byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
    
                return web3j.ethSendRawTransaction(Numeric.toHexString(signMessage)).sendAsync().get().getTransactionHash();
            }catch (Exception e) {
                logger.error([Token offline transfer failed] Error message: {}", e.getMessage());
                return null; }}Copy the code
  • The test code

    	/** * Test ETH transfer */
    	@Test
    	public void testETHTransfer(a) throws Exception{
    		String from = "0xB7Cd09d73a1719b90469Edf7Aa1942d8f89Ba21f";
    		String to = "0xF0B8412C211261B68bc797f31F642Aa14fbDC007";
    		String privateKey = "Key not visible";
    		BigDecimal value = Convert.toWei("1", Convert.Unit.WEI);
    		BigInteger gasPrice = EthUtil.web3j.ethGasPrice().send().getGasPrice();
    		BigInteger gasLimit = EthUtil.web3j.ethEstimateGas(new Transaction(from, null.null.null, to, value.toBigInteger(), null)).send().getAmountUsed();
    		String txid = EthUtil.sendEthTx(from, to, gasLimit, gasPrice, value, Credentials.create(privateKey));
    		logger.warn("txid: {}", txid);
    	}
    
    	/** * Test token transfer */
    	@Test
    	public void testTokenTransfer(a) throws Exception{
    		String from = "0xB7Cd09d73a1719b90469Edf7Aa1942d8f89Ba21f";
    		String to = "0xF0B8412C211261B68bc797f31F642Aa14fbDC007";
    		String contractAddress = "0x6a26797a73f558a09a47d2dd56fbe03227a31dbb";
    		String privateKey = "Key not visible";
    		BigInteger value = BigDecimal.valueOf(Math.pow(10, EthUtil.getTokenDecimal(contractAddress))).toBigInteger();
    		BigInteger gasPrice = EthUtil.web3j.ethGasPrice().send().getGasPrice();
    		BigInteger gasLimit = EthUtil.getTransactionGasLimit(from, to, contractAddress, value);
    		String txid = EthUtil.sendTokenTx(from, to, gasLimit, gasPrice, gasLimit, contractAddress, Credentials.create(privateKey));
    		logger.warn("txid: {}", txid);
    	}
    Copy the code

    The contract address in the code is TestCoin Token(TCT), a TestCoin I published in rinkeby test network. Students who do not want to deploy the contract by themselves can follow my official account and send me the wallet address. I will send some test coins to your wallet address. In the above code, the more interesting is about the management of the nonce value, about the explanation of the Nonce value can refer to this article. In the above code, the nonce value is obtained directly through the RPC interface, which is relatively simple but takes the longest time due to the network overhead of calling RPC. More mature approach is on the trading information to maintain a nonce field in the table, doing so on the one hand is to launch a New Deal can faster get nonce value, on the other hand, an error occurred while trading (launched a mistake with the amount), can be modified in time, because the etheric lane design is: You can initiate a transaction with the same value as the previous nonce to override the pending transaction.

    In addition, regarding the calculation of miner’s fee, normal Ethereum transaction is miner’s fee =gasPrice*gasLimit, where gasPrice is the cost of each calculation step and gasLimit is the maximum number of calculations allowed. Their units are WEI, please refer to this article for the unit of Ethereum. And when you use this formula to calculate the scrip fee is not very accurate, because the consumption of tokens trading gas is not absolutely unused, the trade in the chain of blocks in the browser, the page you can see that each token deal has a gas utilization, and because each token input script size is different, so not sure gas utilization. So far, I haven’t found a way to accurately calculate the fees for tokens.

If you are interested in my article, please follow my official account