# token_nft_admin **Repository Path**: blackcoder/token_nft_admin ## Basic Information - **Project Name**: token_nft_admin - **Description**: nft合约对应的后台 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-07-02 - **Last Updated**: 2024-07-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # token_nft_admin ### 合约项目:https://gitee.com/blackcoder/token_nft.git #### 介绍 - 监听合约事件 - web3j处理REC721和REC720的代币转移 - 钱包登录 #### 软件架构 - springboot2.x - mybatis-plus - Mysql5.7 - redis5.x - swagger3 #### 安装教程 1. jdk8+ 2. Mysql5.7 3. redis5.x 4. maven #### web3j核心功能-链上做交互 ###### 监听(获取)链上信息:1.监听区块高度范围 2.监听的合约地址 `EthLog ethLog = web3j.ethGetLogs(new EthFilter(DefaultBlockParameter.valueOf(startScanBlock), DefaultBlockParameter.valueOf(lastScanBlock), pledgeAddress)).send(); ` ###### 执行链上方法(代币转移,更新链上数据状态......) 1.创建合约对象-获取合约的abi和bin文件。使用第三方工具(web3j)执行命令,最终会得到对应的Class文件. ``` //关于truffle项目中:build/contarcts下有N多个json文件。找对合约名字对应文件。找到abi和bytecode(bin)字段的内容。 格式:web3j generate solidity -b <文件名>.bin -a <文件名>.abi -o <输出目录> -p <包路径> web3j generate solidity -b NftAuction.bin -a NftAuction.abi -o java/ -p com.baiye ``` 2.创建的合约对象,一般实际开发中都是以Bean的形式被初始化 ``` @Bean("mwToken") @SneakyThrows public MwToken mwToken(Web3j web3j) { TransactionManager transactionManager = new RawTransactionManager(web3j, Credentials.create(walletNftRefundPriKey), 1337); MwToken mwToken = MwToken.load(mwTokenContractAddress, web3j, transactionManager, new StaticGasProvider(new BigInteger(gasPriceValue), new BigInteger(gasLimitValue))); return mwToken; } @Bean("mwNftToken") @SneakyThrows public MwNft mwNftToken(Web3j web3j) { TransactionManager transactionManager = new RawTransactionManager(web3j, Credentials.create(walletNftRefundPriKey), 1337); MwNft mwNftToken = MwNft.load(mwNftContractAddress, web3j, transactionManager, new StaticGasProvider(new BigInteger(gasPriceValue), new BigInteger(gasLimitValue))); return mwNftToken; } ``` 3. 调用链上方法: ``` @Autowired private NftAuction nftAuction; @Autowired private MwToken mwToken; @Async public void endAuction() { // 实际:从数据库中加载拍卖的所有过期nft for (MwNftAuctionModel mwNftAuctionModel : mwNftAuctionModelList) { try { if (mwNftAuctionModel.getHighestBid().compareTo(BigDecimal.ZERO) > 0) { BigInteger transferAmount = mwNftAuctionModel.getHighestBid().multiply(BigDecimal.TEN.pow(18)).toBigInteger(); mwToken.approve(nftAuctionContractAddress, transferAmount); RemoteFunctionCall transactionReceiptRemoteFunctionCall = nftAuction.endAuction(new BigInteger(mwNftAuctionModel.getOrderNo())); TransactionReceipt transactionReceipt = transactionReceiptRemoteFunctionCall.send(); boolean statusOK = transactionReceipt.isStatusOK(); if (!statusOK) { log.info("拍卖订单号:{},执行结束失败:{}", mwNftAuctionModel.getOrderNo(),transactionReceipt.getRevertReason()); } } } catch (Exception e) { log.error("拍卖订单号:{},执行结束异常:{}", mwNftAuctionModel.getOrderNo(), e); } } } ``` #### 钱包登录-LoginController 1. 返回一个随机字符串 1. 前端使用MetaMask钱包生成签名 1. 后端验证签名,发放token和授权 ``` @ApiOperation("获取登录随机数") @GetMapping("/getNonce") public Result getNonce(@Validated @NotBlank(message = "address不能是空") String address) { Map map = loginServer.getNonce(address); return ResultGenerator.genSuccessResult(map); } @ApiOperation("登录") @PostMapping("/login") public Result login(@Validated @RequestBody UserLoginDetails userLoginDetails) { String token = loginServer.login(userLoginDetails); return ResultGenerator.genSuccessResult(token); } ``` #### 代币转移-MwTokenController ``` @ApiOperation("转移token") @PostMapping("/transfer") public Result transfer(@RequestBody JSONObject param) { String address = param.getString("address"); BigDecimal mwAmount = param.getBigDecimal("mwAmount"); mwTokenServer.transfer(address, mwAmount); return ResultGenerator.genSuccessResult(); } ``` #### 监听合约事件-MwTokenController 1. 初始化所有合约地址 1. 初始化所有合约事件 1. 区块高度每5秒执行一次,每次扫描100:扫描范围越广,监听耗时越久,影响响应速度。区块高度要持久化(这里举例子使用redis简化),防止项目停止期间,链上有交易。 ``` public void evenLogs(EthLog ethLog) { if (!CollectionUtils.isEmpty(ethLog.getLogs())) { ethLog.getLogs().forEach(logResult -> { try { processData((Log) logResult.get()); } catch (Exception e) { log.info("处理日志失败:{}", e); } }); } } ```