Java NFT案例

wen java案例 1

本文目录导读:

Java NFT案例

  1. 项目结构
  2. Maven依赖配置 (pom.xml)
  3. 配置文件 (application.yml)
  4. 配置类
  5. 数据模型
  6. IPFS服务
  7. 以太坊智能合约服务
  8. NFT服务
  9. Controller
  10. 主启动类
  11. 使用示例
  12. 智能合约示例(Solidity)
  13. 部署说明

我来为您提供一个完整的Java NFT案例,包含智能合约交互、铸币、查询等核心功能。

项目结构

nft-project/
├── pom.xml
└── src/
    └── main/
        └── java/
            └── com/
                └── example/
                    └── nft/
                        ├── config/
                        ├── model/
                        ├── service/
                        └── controller/

Maven依赖配置 (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>nft-project</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.14</version>
        </dependency>
        <!-- Web3j - Ethereum 交互 -->
        <dependency>
            <groupId>org.web3j</groupId>
            <artifactId>core</artifactId>
            <version>4.9.8</version>
        </dependency>
        <!-- IPFS -->
        <dependency>
            <groupId>com.github.ipfs</groupId>
            <artifactId>java-ipfs-http-client</artifactId>
            <version>1.4.4</version>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
            <optional>true</optional>
        </dependency>
        <!-- 配置文件处理 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

配置文件 (application.yml)

server:
  port: 8080
spring:
  application:
    name: nft-service
nft:
  ethereum:
    # RPC节点地址(本地或Infura)
    rpc-url: ${RPC_URL:http://localhost:8545}
    # 合约地址
    contract-address: ${CONTRACT_ADDRESS:0x0000000000000000000000000000000000000000}
    # 钱包私钥
    private-key: ${PRIVATE_KEY:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
    # Gas 设置
    gas-price: 20000000000
    gas-limit: 3000000
  ipfs:
    # IPFS节点地址
    host: ${IPFS_HOST:localhost}
    port: ${IPFS_PORT:5001}
  # 合约ABI
  contract-abi: |
    [
      {
        "constant": false,
        "inputs": [
          {"name": "tokenURI", "type": "string"}
        ],
        "name": "mintNFT",
        "outputs": [
          {"name": "", "type": "uint256"}
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      }
    ]

配置类

package com.example.nft.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "nft.ethereum")
public class EthereumConfig {
    private String rpcUrl;
    private String contractAddress;
    private String privateKey;
    private Long gasPrice;
    private Long gasLimit;
}

数据模型

package com.example.nft.model;
import lombok.Data;
import java.math.BigInteger;
@Data
public class NFTMetadata {
    private String name;
    private String description;
    private String image;
    private String externalUrl;
    private String[] attributes;
}
@Data
public class NFTToken {
    private BigInteger tokenId;
    private String tokenURI;
    private String owner;
    private NFTMetadata metadata;
}
@Data
public class MintRequest {
    private String name;
    private String description;
    private String image;
    private String receiverAddress;
}

IPFS服务

package com.example.nft.service;
import io.ipfs.api.IPFS;
import io.ipfs.api.MerkleNode;
import io.ipfs.multihash.Multihash;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
public class IPFSService {
    private final IPFS ipfs;
    public IPFSService() {
        // 连接IPFS节点
        this.ipfs = new IPFS("localhost", 5001);
    }
    /**
     * 上传文件到IPFS
     */
    public String uploadFile(MultipartFile file) throws Exception {
        byte[] content = file.getBytes();
        MerkleFile addResult = ipfs.add(content);
        return addResult.hash.toString();
    }
    /**
     * 上传JSON元数据到IPFS
     */
    public String uploadMetadata(String jsonMetadata) throws Exception {
        byte[] content = jsonMetadata.getBytes();
        MerkleNode addResult = ipfs.add(content);
        return addResult.hash.toString();
    }
    /**
     * 从IPFS获取内容
     */
    public byte[] getContent(String hash) throws Exception {
        Multihash fileHash = Multihash.fromBase58(hash);
        return ipfs.cat(fileHash);
    }
}

以太坊智能合约服务

package com.example.nft.service;
import com.example.nft.config.EthereumConfig;
import com.example.nft.model.NFTMetadata;
import com.example.nft.model.NFTToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.web3j.abi.DefaultFunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Uint;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.EthGasPrice;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Slf4j
@Service
public class EthereumService {
    private final Web3j web3j;
    private final Credentials credentials;
    private final EthereumConfig config;
    private final ObjectMapper objectMapper;
    public EthereumService(EthereumConfig config, ObjectMapper objectMapper) {
        this.config = config;
        this.objectMapper = objectMapper;
        this.web3j = Web3j.build(new HttpService(config.getRpcUrl()));
        this.credentials = Credentials.create(config.getPrivateKey());
    }
    /**
     * 铸币(Mint)NFT
     */
    public String mintNFT(String receiverAddress, String tokenURI) throws Exception {
        try {
            // 构造mintNFT函数调用
            Function function = new Function(
                "mintNFT",
                Arrays.asList(new Utf8String(tokenURI)),
                Collections.singletonList(new TypeReference<Uint>() {})
            );
            // 生成交易数据
            String encodedFunction = DefaultFunctionReturnDecoder.encodeFunction(function);
            // 获取nonce
            BigInteger nonce = getNonce(credentials.getAddress());
            // 获取gas价格
            EthGasPrice gasPrice = web3j.ethGasPrice().send();
            // 创建交易
            Transaction transaction = Transaction.createFunctionCallTransaction(
                credentials.getAddress(),           // from
                nonce,                              // nonce
                gasPrice.getGasPrice(),             // gas price
                BigInteger.valueOf(3000000),        // gas limit
                config.getContractAddress(),        // to - 合约地址
                encodedFunction                     // data
            );
            // 签名并发送交易
            signedTransaction signedTransaction = web3j.ethAccountSign(
                transaction, credentials).send();
            // 发送交易
            EthSendTransaction ethSendTransaction = web3j.ethSendTransaction(transaction).send();
            if (ethSendTransaction.hasError()) {
                throw new RuntimeException("Transaction failed: " + 
                    ethSendTransaction.getError().getMessage());
            }
            log.info("NFT minted successfully. TxHash: {}", 
                ethSendTransaction.getTransactionHash());
            return ethSendTransaction.getTransactionHash();
        } catch (Exception e) {
            log.error("Failed to mint NFT", e);
            throw e;
        }
    }
    /**
     * 获取NFT URI
     */
    public String getTokenURI(BigInteger tokenId) throws Exception {
        Function function = new Function(
            "tokenURI",
            Arrays.asList(new Uint(tokenId)),
            Collections.singletonList(new TypeReference<Utf8String>() {})
        );
        String encodedFunction = DefaultFunctionReturnDecoder.encodeFunction(function);
        Transaction transaction = Transaction.createEthCallTransaction(
            credentials.getAddress(),
            config.getContractAddress(),
            encodedFunction
        );
        EthCall ethCall = web3j.ethCall(
            transaction, 
            DefaultBlockParameterName.LATEST
        ).send();
        if (ethCall.hasError()) {
            throw new RuntimeException("Call failed: " + 
                ethCall.getError().getMessage());
        }
        String decodedValue = DefaultFunctionReturnDecoder.decodeFunctionResult(
            function, ethCall.getValue()
        ).get(0).getValue().toString();
        return decodedValue;
    }
    /**
     * 获取当前nonce
     */
    private BigInteger getNonce(String address) throws Exception {
        return web3j.ethGetTransactionCount(
            address, 
            DefaultBlockParameterName.LATEST
        ).send().getTransactionCount();
    }
}

NFT服务

package com.example.nft.service;
import com.example.nft.model.MintRequest;
import com.example.nft.model.NFTMetadata;
import com.example.nft.model.NFTToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigInteger;
@Slf4j
@Service
public class NFTService {
    private final IPFSService ipfsService;
    private final EthereumService ethereumService;
    private final ObjectMapper objectMapper;
    public NFTService(IPFSService ipfsService, 
                     EthereumService ethereumService,
                     ObjectMapper objectMapper) {
        this.ipfsService = ipfsService;
        this.ethereumService = ethereumService;
        this.objectMapper = objectMapper;
    }
    /**
     * 创建NFT(铸币)
     */
    public String createNFT(MintRequest request, MultipartFile image) throws Exception {
        log.info("Creating NFT: {}", request.getName());
        // 1. 上传图片到IPFS
        String imageHash = ipfsService.uploadFile(image);
        String imageUrl = "ipfs://" + imageHash;
        // 2. 创建元数据
        NFTMetadata metadata = new NFTMetadata();
        metadata.setName(request.getName());
        metadata.setDescription(request.getDescription());
        metadata.setImage(imageUrl);
        // 3. 上传元数据到IPFS
        String metadataJson = objectMapper.writeValueAsString(metadata);
        String metadataHash = ipfsService.uploadMetadata(metadataJson);
        String tokenURI = "ipfs://" + metadataHash;
        // 4. 调用智能合约铸币
        String txHash = ethereumService.mintNFT(
            request.getReceiverAddress(), 
            tokenURI
        );
        log.info("NFT created successfully. TxHash: {}", txHash);
        return txHash;
    }
    /**
     * 获取NFT信息
     */
    public NFTToken getNFT(BigInteger tokenId) throws Exception {
        log.info("Getting NFT info for token: {}", tokenId);
        // 1. 从链上获取tokenURI
        String tokenURI = ethereumService.getTokenURI(tokenId);
        // 2. 从IPFS获取元数据
        String hash = tokenURI.replace("ipfs://", "");
        byte[] metadataBytes = ipfsService.getContent(hash);
        NFTMetadata metadata = objectMapper.readValue(
            metadataBytes, 
            NFTMetadata.class
        );
        // 3. 组装返回
        NFTToken token = new NFTToken();
        token.setTokenId(tokenId);
        token.setTokenURI(tokenURI);
        token.setMetadata(metadata);
        return token;
    }
}

Controller

package com.example.nft.controller;
import com.example.nft.model.MintRequest;
import com.example.nft.model.NFTToken;
import com.example.nft.service.NFTService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/nft")
public class NFTController {
    private final NFTService nftService;
    public NFTController(NFTService nftService) {
        this.nftService = nftService;
    }
    /**
     * 创建NFT
     */
    @PostMapping("/mint")
    public ResponseEntity<Map<String, String>> mintNFT(
            @RequestParam("name") String name,
            @RequestParam("description") String description,
            @RequestParam("image") MultipartFile image,
            @RequestParam("receiverAddress") String receiverAddress) {
        try {
            MintRequest request = new MintRequest();
            request.setName(name);
            request.setDescription(description);
            request.setReceiverAddress(receiverAddress);
            String txHash = nftService.createNFT(request, image);
            Map<String, String> response = new HashMap<>();
            response.put("status", "success");
            response.put("transactionHash", txHash);
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            log.error("Failed to mint NFT", e);
            Map<String, String> response = new HashMap<>();
            response.put("status", "error");
            response.put("message", e.getMessage());
            return ResponseEntity.internalServerError().body(response);
        }
    }
    /**
     * 查询NFT
     */
    @GetMapping("/{tokenId}")
    public ResponseEntity<NFTToken> getNFT(@PathVariable BigInteger tokenId) {
        try {
            NFTToken token = nftService.getNFT(tokenId);
            return ResponseEntity.ok(token);
        } catch (Exception e) {
            log.error("Failed to get NFT", e);
            return ResponseEntity.internalServerError().build();
        }
    }
    /**
     * 健康检查
     */
    @GetMapping("/health")
    public ResponseEntity<Map<String, String>> healthCheck() {
        Map<String, String> response = new HashMap<>();
        response.put("status", "NFT Service is running");
        return ResponseEntity.ok(response);
    }
}

主启动类

package com.example.nft;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties
public class NFTApplication {
    public static void main(String[] args) {
        SpringApplication.run(NFTApplication.class, args);
        System.out.println("NFT Service started successfully!");
    }
}

使用示例

1 创建NFT(curl示例)

curl -X POST http://localhost:8080/api/nft/mint \
  -F "name=My First NFT" \
  -F "description=This is my first NFT on blockchain" \
  -F "image=@/path/to/your/image.png" \
  -F "receiverAddress=0xYourWalletAddress"

2 查询NFT

curl http://localhost:8080/api/nft/1

智能合约示例(Solidity)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract MyNFT is ERC721, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    // IPFS路径映射
    mapping(uint256 => string) private _tokenURIs;
    constructor() ERC721("MyNFT", "MNFT") {}
    function mintNFT(address recipient, string memory tokenURI) 
        public 
        returns (uint256) 
    {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();
        _mint(recipient, newItemId);
        _setTokenURI(newItemId, tokenURI);
        return newItemId;
    }
    function tokenURI(uint256 tokenId) 
        public 
        view 
        override 
        returns (string memory) 
    {
        return _tokenURIs[tokenId];
    }
    function _setTokenURI(uint256 tokenId, string memory tokenURI) 
        internal 
    {
        _tokenURIs[tokenId] = tokenURI;
    }
}

部署说明

  1. 环境准备

    • 安装Ganache(本地以太坊模拟器)
    • 安装IPFS节点
    • 部署智能合约
  2. 启动服务

    # 编译并运行
    mvn clean package
    java -jar target/nft-project-1.0.0.jar
  3. 配置环境变量

    export RPC_URL=http://localhost:8545
    export CONTRACT_ADDRESS=0x你的合约地址
    export PRIVATE_KEY=你的私钥

这个完整的NFT案例涵盖了:

  • 与以太坊智能合约的交互
  • IPFS去中心化存储
  • NFT元数据管理
  • 铸币(Mint)功能
  • RESTful API接口

您可以根据实际需求调整代码,比如添加NFT交易市场功能、拍卖功能等。

抱歉,评论功能暂时关闭!