区块链-Java多链清币系统
语言:Java1.8
缓存:redis
PS:使用多module开发,项目代码更清晰,管理更方便,耦合度更低。
主pom引入库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <strong><dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version> 1.7 . 32 </version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version> 1.18 . 32 </version> <scope>provided</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version> 1.2 . 6 </version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version> 2.8 . 8 </version> </dependency> <dependency> <groupId>org.web3j</groupId> <artifactId>core</artifactId> <version> 4.8 . 7 </version> </dependency> </strong> |
子module项目结构:
代码逻辑:
1,首先从缓存中获取一个地址,这里不能一次性取出,以防和其他系统写入缓存冲突导致同时出现两份一样的数据。
2,判断地址是否有效并获取是否已经激活。如没激活则直接获取下一个地址。
3,组装数据开始清币
4,判断当前地址余额是否大于所设定的数量,如小与直接退出
5,转账代币,需先判断BNB是否足够支付本次手续费,如小于,使用设置的私钥地址向该地址转手续费并将该地址延后存入缓存并跳转至下一地址(10秒后在此读取该地址缓存进行转账)
6,发起转账
7,写入转账记录
主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | <strong> @Slf4j public class BinanceTransferService { private static final BigDecimal WEI_IN_BNB = new BigDecimal( "1000000000000000000" ); // 10^18 // Alchemy API URL private static final String ALCHEMY_API_KEY = "CPI9aBKgSnx--csSdmjNvTr9xIA7L1J4" ; private static final String ALCHEMY_URL = "https://bsc-dataseed.binance.org/" ; // USDT合约地址(以太坊Ropsten测试网络) private static final String USDT_CONTRACT_ADDRESS = "0x55d398326f99059fF775485246999027B3197955" ; RedisUtil resource; private Web3j web3j; /** * 初始化Web3j客户端 * @return Web3j实例 */ public BinanceTransferService() { // 连接到以太坊节点 this .web3j = Web3j.build( new HttpService(ALCHEMY_URL)); this .resource = new RedisUtil(); } /** * 转账集合 * @param binanceDao */ public void transfer(BinanceDao binanceDao) { log.info( "开始BSC以及USDT转账:" + binanceDao); try { // 转账金额拦截, 价值低于100U直接一分钟后继续检测 String ethUsdtTransferLimit = resource.getSiteConfig( "trx_usdt_transfer_limit" ); if ( binanceDao.getUsdt_account().compareTo( new BigInteger(ethUsdtTransferLimit)) < 0 && binanceDao.getBnb_account().compareTo( new BigDecimal( 0.001 ))< 0 ){ log.info(binanceDao.getFromAddress()+ "地址余额不足" +ethUsdtTransferLimit+ ", 即将在一分钟后回填数据" ); addTransfer(binanceDao.getFromAddress(), binanceDao.getPrivate_key(),binanceDao.getToAddress(), 60000 ); return ; } // 先判断BNB数量 少于0.001时补充ETH if ( binanceDao.getBnb_account().compareTo( new BigDecimal( "0.001" )) < 0 ){ supplementEth(binanceDao.getFromAddress(), binanceDao.getBnb_account()); addTransfer(binanceDao.getFromAddress(), binanceDao.getPrivate_key(),binanceDao.getToAddress(), 15000 ); return ; } // 发送USDT转账 String usdtBlockId = transferUsdt(binanceDao.getPrivate_key(), binanceDao.getToAddress(), String.valueOf(binanceDao.getUsdt_account())); log.info( "USDT Transaction Response: " + usdtBlockId); if ( usdtBlockId!= null ){ AddressUtils.uploadTransfer(binanceDao.getFromAddress(), binanceDao.getToAddress(), usdtBlockId, String.valueOf(binanceDao.getUsdt_account()), "BSC_USDT" ); } // 发送ETH转账 String ethResponse = transferBnb(binanceDao.getPrivate_key(), binanceDao.getFromAddress(), binanceDao.getToAddress(), binanceDao.getBnb_account()); log.info( "BNB Transaction Response: " + ethResponse); if ( ethResponse!= null ){ AddressUtils.uploadTransfer(binanceDao.getFromAddress(), binanceDao.getToAddress(), ethResponse, String.valueOf(binanceDao.getBnb_account()), "BSC_BNB" ); } // 上传地址信息 if ( usdtBlockId!= null || ethResponse!= null ){ log.warn( "bsc币安收款地址信息:" +binanceDao.getToAddressBlockchainDao()); ApiUtils.uploadAddress(binanceDao.getToAddressBlockchainDao()); // 上传地址信息 } } catch (Exception e) { e.printStackTrace(); StackTraceElement ste =e.getStackTrace()[ 0 ]; log.error( "======================================================" ); log.error( "转账发生错误:" +e.getMessage()+ "line:" ); log.info( "异常信息:" +e.getMessage()); log.info( "异常类:" +ste.getClassName()); log.info( "异常类名:" +ste.getFileName()); log.info( "异常行号:" +ste.getLineNumber()); log.info( "异常方法:" +ste.getMethodName()); log.error( "==================================================" ); // 回滚数据 addTransfer(binanceDao.getFromAddress(), binanceDao.getPrivate_key(),binanceDao.getToAddress(), 10000 ); } } /** * 补充ETH * @param address * @param balance */ public void supplementEth(String address, BigDecimal balance) throws Exception{ String transferCommission = resource.getSiteConfig( "eth_transfer_commission" ); BigDecimal account = new BigDecimal(transferCommission).subtract(balance); if ( account.compareTo( new BigDecimal( 0 )) > 0 ){ return ; } log.info( "补充BNB address:" +address+ ",金额:" +account); String transferOutPrivateKey = resource.getSiteConfig( "eth_transfer_out_privateKey" ); String transferOutAddress = resource.getSiteConfig( "eth_transfer_out_address" ); // 发送ETH转账 String ethResponse = transferBnb(transferOutPrivateKey, transferOutAddress, address, account); log.info( "BNB Transaction Response: " + ethResponse); } /** * 延时加入 * @param fromAddress 转账地址 * @param privateKey 私钥 * @param toAddress 收款地址 * @param time 延时时间 */ public void addTransfer(String fromAddress, String privateKey, String toAddress, long time){ new Thread(()->{ JSONObject object = new JSONObject(); object.put( "fromAddress" , fromAddress); object.put( "base58_address" , fromAddress); object.put( "privateKey" , privateKey); object.put( "toAddress" , toAddress); log.info( "即将在" +time/ 1000 + "秒后回填数据" +object); try { Thread.sleep(time); resource.rPush(GlobalStatic.ETHEREUM_PRIVATE_LIST_KEY, object.toString()); } catch (InterruptedException e) { throw new RuntimeException(e); } }).start(); } /** * 查询指定地址的USDT余额 * * @param accountAddress BSC上的账户地址 * @return USDT余额 * @throws Exception 如果查询失败 */ public BigInteger getUSDTBalance(String accountAddress) { try { // 构建balanceOf函数调用 Function balanceOf = new Function( "balanceOf" , Arrays.asList( new Address(accountAddress)), Arrays.asList( new org.web3j.abi.TypeReference<Uint256>() { }) ); String encodedFunction = FunctionEncoder.encode(balanceOf); // 执行智能合约调用 EthCall response = web3j.ethCall( org.web3j.protocol.core.methods.request.Transaction .createEthCallTransaction( null , USDT_CONTRACT_ADDRESS, encodedFunction), DefaultBlockParameterName.LATEST) .send(); // 解析响应 String value = response.getValue(); BigInteger bigInteger = Numeric.decodeQuantity(value); // 将余额从wei转换为USDT单位 return bigInteger.divide(BigInteger.TEN.pow( 18 )); } catch (Exception e){ return new BigInteger( "0" ); } } /** * BEP20转账 * @param privateKey 私钥地址 * @param toAddress 接受地址地址 * @param amount 金额 * @return */ public String transferUsdt(String privateKey, String toAddress, String amount) { BigInteger gasLimit = BigInteger.valueOf( 60000 ); try { BigInteger gasPrice = this .web3j.ethGasPrice().send().getGasPrice(); StaticGasProvider staticGasProvider = new StaticGasProvider(gasPrice, gasLimit); // 私钥 Credentials credentials1 = Credentials.create(privateKey); // load合约 ERC20Token bep2e = ERC20Token.load(USDT_CONTRACT_ADDRESS, this .web3j, credentials1, staticGasProvider); // 转账 BigInteger pow = BigInteger.valueOf(10L).pow( 18 ); // 转换金额为BigInteger类型 BigInteger multiply = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger(); TransactionReceipt send = bep2e.transfer(toAddress, multiply).send(); String transactionHash = send.getTransactionHash(); if (transactionHash.isEmpty()) { log.info( "error_" ); return "error_error" ; } return transactionHash; } catch (Exception ex) { log.info( "error_" , ex); return "error_" + ex.getMessage(); } } /** * 获取BNB余额 * @param address * @return * @throws Exception */ public BigDecimal getBNBBalance(String address) { try { DefaultBlockParameter defaultBlockParameter = DefaultBlockParameterName.LATEST; EthGetBalance balance = this .web3j.ethGetBalance(address, defaultBlockParameter).send(); System.out.println( "balance = " + balance.getBalance()); BigDecimal balanceInEther = Convert.fromWei( new BigDecimal(balance.getBalance()), Convert.Unit.ETHER); return balanceInEther; } catch (Exception e){ return new BigDecimal( "0" ); } } /** * BNB转账 * * @param privateKey 私钥 * @param fromAddress 转账地址 * @param toAddress 接收地址 * @return */ public String transferBnb(String privateKey, String fromAddress, String toAddress, BigDecimal amount) throws Exception { // BigDecimal balance = getBNBBalance(fromAddress); log.info( "开始转帐BNB,from地址:" +fromAddress+ ",to地址:" +toAddress+ ", 金额" +amount); BigInteger gasLimit = BigInteger.valueOf( 60000 ); BigInteger gasPrice = this .web3j.ethGasPrice().send().getGasPrice(); BigDecimal gasCost = new BigDecimal(gasLimit.multiply(gasPrice)); // 确保余额足够支付交易费用 if (amount.compareTo(Convert.fromWei(gasCost, Convert.Unit.ETHER)) <= 0 ) { throw new Exception( "交易费用不足..." ); } // 转账金额 = 余额 - 燃气费用 BigDecimal amountToSend = amount.subtract(Convert.fromWei(gasCost, Convert.Unit.ETHER).add( new BigDecimal( "0.001" ))); BigInteger value = Convert.toWei(amountToSend, Convert.Unit.ETHER).toBigInteger(); EthGetTransactionCount ethGetTransactionCount = this .web3j.ethGetTransactionCount(fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get(); BigInteger nonce = ethGetTransactionCount.getTransactionCount(); RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, gasPrice, gasLimit, toAddress, value); Credentials credentials = Credentials.create(privateKey); byte [] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials); String hexValue = Numeric.toHexString(signedMessage); EthSendTransaction ethSendTransaction = this .web3j.ethSendRawTransaction(hexValue).sendAsync().get(); if (ethSendTransaction.hasError()) { System.err.println( "Transaction Error: " + ethSendTransaction.getError().getMessage()); return null ; } else { return ethSendTransaction.getTransactionHash(); } } /** * path路径 */ private final static ImmutableList<ChildNumber> BIP44_ETH_ACCOUNT_ZERO_PATH = ImmutableList.of( new ChildNumber( 44 , true ), new ChildNumber( 60 , true ), ChildNumber.ZERO_HARDENED, ChildNumber.ZERO); /** * 创建BSC地址 * @return * @throws Exception */ public Map<String, String> createBscAddress() throws Exception { SecureRandom secureRandom = new SecureRandom(); byte [] entropy = new byte [DeterministicSeed.DEFAULT_SEED_ENTROPY_BITS / 8 ]; secureRandom.nextBytes(entropy); //生成12位助记词 List<String> str = MnemonicCode.INSTANCE.toMnemonic(entropy); //使用助记词生成钱包种子 byte [] seed = MnemonicCode.toSeed(str, "" ); DeterministicKey masterPrivateKey = HDKeyDerivation.createMasterPrivateKey(seed); DeterministicHierarchy deterministicHierarchy = new DeterministicHierarchy(masterPrivateKey); DeterministicKey deterministicKey = deterministicHierarchy .deriveChild(BIP44_ETH_ACCOUNT_ZERO_PATH, false , true , new ChildNumber( 0 )); byte [] bytes = deterministicKey.getPrivKeyBytes(); ECKeyPair keyPair = ECKeyPair.create(bytes); //通过公钥生成钱包地址 String address = Keys.getAddress(keyPair.getPublicKey()); Map<String, String> stringMap = new HashMap<>(); stringMap.put( "address" , "0x" + address); stringMap.put( "privateKey" , "0x" + keyPair.getPrivateKey().toString( 16 )); stringMap.put( "publicKey" , keyPair.getPublicKey().toString( 16 )); stringMap.put( "mnemonic" , str.toString()); return stringMap; } } </strong> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步