google-site-verification: google61fe8ba583a51912.html
NFT Minting API in Java

Overview

This guide covers building a comprehensive NFT (Non-Fungible Token) minting API in Java using Spring Boot, with support for multiple blockchains (Ethereum, Solana, Polygon) and IPFS for metadata storage.

1. Project Setup and Dependencies

Maven Dependencies

<dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- Blockchain Integration --> <dependency> <groupId>org.web3j</groupId> <artifactId>core</artifactId> <version>4.9.7</version> </dependency> <!-- IPFS --> <dependency> <groupId>io.ipfs.api</groupId> <artifactId>ipfs-api</artifactId> <version>1.4.0</version> </dependency> <!-- Solana --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.10.0</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- Database --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <!-- Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- JWT --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> </dependencies>

2. Data Models and Entities

import javax.persistence.*; import javax.validation.constraints.*; import java.math.BigInteger; import java.time.LocalDateTime; import java.util.*; @Entity @Table(name = "nft_collections") public class NFTCollection { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank @Column(unique = true) private String name; private String description; @NotBlank private String symbol; @Enumerated(EnumType.STRING) private Blockchain blockchain; @NotBlank private String contractAddress; private String ownerAddress; @OneToMany(mappedBy = "collection", cascade = CascadeType.ALL) private List<NFT> nfts = new ArrayList<>(); private LocalDateTime createdAt; private LocalDateTime updatedAt; @PrePersist protected void onCreate() { createdAt = LocalDateTime.now(); updatedAt = LocalDateTime.now(); } @PreUpdate protected void onUpdate() { updatedAt = LocalDateTime.now(); } // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getSymbol() { return symbol; } public void setSymbol(String symbol) { this.symbol = symbol; } public Blockchain getBlockchain() { return blockchain; } public void setBlockchain(Blockchain blockchain) { this.blockchain = blockchain; } public String getContractAddress() { return contractAddress; } public void setContractAddress(String contractAddress) { this.contractAddress = contractAddress; } public String getOwnerAddress() { return ownerAddress; } public void setOwnerAddress(String ownerAddress) { this.ownerAddress = ownerAddress; } public List<NFT> getNfts() { return nfts; } public void setNfts(List<NFT> nfts) { this.nfts = nfts; } public LocalDateTime getCreatedAt() { return createdAt; } public LocalDateTime getUpdatedAt() { return updatedAt; } } @Entity @Table(name = "nfts") public class NFT { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank private String name; private String description; @NotNull private BigInteger tokenId; @NotBlank private String tokenUri; @NotBlank private String ipfsHash; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "collection_id") private NFTCollection collection; @NotBlank private String creatorAddress; private String ownerAddress; @Enumerated(EnumType.STRING) private NFTStatus status = NFTStatus.PENDING; @ElementCollection @CollectionTable(name = "nft_attributes", joinColumns = @JoinColumn(name = "nft_id")) private List<NFTAttribute> attributes = new ArrayList<>(); private String transactionHash; private BigInteger gasUsed; private BigInteger gasPrice; private LocalDateTime createdAt; private LocalDateTime updatedAt; private LocalDateTime mintedAt; @PrePersist protected void onCreate() { createdAt = LocalDateTime.now(); updatedAt = LocalDateTime.now(); } @PreUpdate protected void onUpdate() { updatedAt = LocalDateTime.now(); } // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public BigInteger getTokenId() { return tokenId; } public void setTokenId(BigInteger tokenId) { this.tokenId = tokenId; } public String getTokenUri() { return tokenUri; } public void setTokenUri(String tokenUri) { this.tokenUri = tokenUri; } public String getIpfsHash() { return ipfsHash; } public void setIpfsHash(String ipfsHash) { this.ipfsHash = ipfsHash; } public NFTCollection getCollection() { return collection; } public void setCollection(NFTCollection collection) { this.collection = collection; } public String getCreatorAddress() { return creatorAddress; } public void setCreatorAddress(String creatorAddress) { this.creatorAddress = creatorAddress; } public String getOwnerAddress() { return ownerAddress; } public void setOwnerAddress(String ownerAddress) { this.ownerAddress = ownerAddress; } public NFTStatus getStatus() { return status; } public void setStatus(NFTStatus status) { this.status = status; } public List<NFTAttribute> getAttributes() { return attributes; } public void setAttributes(List<NFTAttribute> attributes) { this.attributes = attributes; } public String getTransactionHash() { return transactionHash; } public void setTransactionHash(String transactionHash) { this.transactionHash = transactionHash; } public BigInteger getGasUsed() { return gasUsed; } public void setGasUsed(BigInteger gasUsed) { this.gasUsed = gasUsed; } public BigInteger getGasPrice() { return gasPrice; } public void setGasPrice(BigInteger gasPrice) { this.gasPrice = gasPrice; } public LocalDateTime getCreatedAt() { return createdAt; } public LocalDateTime getUpdatedAt() { return updatedAt; } public LocalDateTime getMintedAt() { return mintedAt; } public void setMintedAt(LocalDateTime mintedAt) { this.mintedAt = mintedAt; } } @Embeddable public class NFTAttribute { @NotBlank private String traitType; private String value; private String displayType; // Getters and setters public String getTraitType() { return traitType; } public void setTraitType(String traitType) { this.traitType = traitType; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getDisplayType() { return displayType; } public void setDisplayType(String displayType) { this.displayType = displayType; } } enum Blockchain { ETHEREUM, POLYGON, SOLANA, BINANCE_SMART_CHAIN } enum NFTStatus { PENDING, MINTING, MINTED, FAILED, BURNED }

3. DTOs and Request/Response Classes

import javax.validation.constraints.*; import java.math.BigInteger; import java.util.*; public class NFTMintRequest { @NotBlank private String name; private String description; @NotNull private Long collectionId; @NotBlank private String creatorAddress; private String ownerAddress; private List<AttributeRequest> attributes = new ArrayList<>(); @NotNull private BigInteger tokenId; // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Long getCollectionId() { return collectionId; } public void setCollectionId(Long collectionId) { this.collectionId = collectionId; } public String getCreatorAddress() { return creatorAddress; } public void setCreatorAddress(String creatorAddress) { this.creatorAddress = creatorAddress; } public String getOwnerAddress() { return ownerAddress; } public void setOwnerAddress(String ownerAddress) { this.ownerAddress = ownerAddress; } public List<AttributeRequest> getAttributes() { return attributes; } public void setAttributes(List<AttributeRequest> attributes) { this.attributes = attributes; } public BigInteger getTokenId() { return tokenId; } public void setTokenId(BigInteger tokenId) { this.tokenId = tokenId; } } public class AttributeRequest { @NotBlank private String traitType; private String value; private String displayType; // Getters and setters public String getTraitType() { return traitType; } public void setTraitType(String traitType) { this.traitType = traitType; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getDisplayType() { return displayType; } public void setDisplayType(String displayType) { this.displayType = displayType; } } public class NFTResponse { private Long id; private String name; private String description; private BigInteger tokenId; private String tokenUri; private String ipfsHash; private String collectionName; private String creatorAddress; private String ownerAddress; private String status; private List<AttributeResponse> attributes; private String transactionHash; private LocalDateTime createdAt; private LocalDateTime mintedAt; // Constructors, getters, and setters public NFTResponse() {} public NFTResponse(NFT nft) { this.id = nft.getId(); this.name = nft.getName(); this.description = nft.getDescription(); this.tokenId = nft.getTokenId(); this.tokenUri = nft.getTokenUri(); this.ipfsHash = nft.getIpfsHash(); this.collectionName = nft.getCollection().getName(); this.creatorAddress = nft.getCreatorAddress(); this.ownerAddress = nft.getOwnerAddress(); this.status = nft.getStatus().name(); this.attributes = nft.getAttributes().stream() .map(AttributeResponse::new) .collect(Collectors.toList()); this.transactionHash = nft.getTransactionHash(); this.createdAt = nft.getCreatedAt(); this.mintedAt = nft.getMintedAt(); } // Getters and setters // ... (omitted for brevity) } public class AttributeResponse { private String traitType; private String value; private String displayType; public AttributeResponse(NFTAttribute attribute) { this.traitType = attribute.getTraitType(); this.value = attribute.getValue(); this.displayType = attribute.getDisplayType(); } // Getters and setters // ... (omitted for brevity) } public class CollectionRequest { @NotBlank private String name; private String description; @NotBlank private String symbol; @NotNull private Blockchain blockchain; @NotBlank private String contractAddress; @NotBlank private String ownerAddress; // Getters and setters // ... (omitted for brevity) }

4. IPFS Service for Metadata Storage

import io.ipfs.api.IPFS; import io.ipfs.api.MerkleNode; import io.ipfs.api.NamedStreamable; import io.ipfs.multihash.Multihash; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.util.*; @Service public class IPFSService { private final IPFS ipfs; private final ObjectMapper objectMapper; public IPFSService(@Value("${ipfs.host:localhost}") String ipfsHost, @Value("${ipfs.port:5001}") int ipfsPort) { this.ipfs = new IPFS(ipfsHost, ipfsPort); this.objectMapper = new ObjectMapper(); } public String uploadMetadata(NFTMetadata metadata) throws IOException { String jsonMetadata = objectMapper.writeValueAsString(metadata); NamedStreamable.ByteArrayWrapper file = new NamedStreamable.ByteArrayWrapper(metadata.getName() + ".json", jsonMetadata.getBytes()); List<MerkleNode> result = ipfs.add(file); if (!result.isEmpty()) { return result.get(0).hash.toBase58(); } throw new IOException("Failed to upload metadata to IPFS"); } public String uploadImage(byte[] imageData, String filename) throws IOException { NamedStreamable.ByteArrayWrapper file = new NamedStreamable.ByteArrayWrapper(filename, imageData); List<MerkleNode> result = ipfs.add(file); if (!result.isEmpty()) { return result.get(0).hash.toBase58(); } throw new IOException("Failed to upload image to IPFS"); } public byte[] getFile(String ipfsHash) throws IOException { Multihash filePointer = Multihash.fromBase58(ipfsHash); return ipfs.cat(filePointer); } public NFTMetadata getMetadata(String ipfsHash) throws IOException { byte[] data = getFile(ipfsHash); return objectMapper.readValue(data, NFTMetadata.class); } } class NFTMetadata { private String name; private String description; private String image; private List<NFTAttribute> attributes; private String externalUrl; private String animationUrl; // Constructors public NFTMetadata() {} public NFTMetadata(String name, String description, String image, List<NFTAttribute> attributes) { this.name = name; this.description = description; this.image = image; this.attributes = attributes; } // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getImage() { return image; } public void setImage(String image) { this.image = image; } public List<NFTAttribute> getAttributes() { return attributes; } public void setAttributes(List<NFTAttribute> attributes) { this.attributes = attributes; } public String getExternalUrl() { return externalUrl; } public void setExternalUrl(String externalUrl) { this.externalUrl = externalUrl; } public String getAnimationUrl() { return animationUrl; } public void setAnimationUrl(String animationUrl) { this.animationUrl = animationUrl; } }

5. Blockchain Service Abstraction

import org.springframework.stereotype.Service; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.tx.gas.DefaultGasProvider; import java.math.BigInteger; import java.util.concurrent.CompletableFuture; @Service public class BlockchainService { private final EthereumService ethereumService; private final PolygonService polygonService; private final SolanaService solanaService; public BlockchainService(EthereumService ethereumService, PolygonService polygonService, SolanaService solanaService) { this.ethereumService = ethereumService; this.polygonService = polygonService; this.solanaService = solanaService; } public CompletableFuture<String> mintNFT(NFT nft, String privateKey) { switch (nft.getCollection().getBlockchain()) { case ETHEREUM: return ethereumService.mintNFT(nft, privateKey); case POLYGON: return polygonService.mintNFT(nft, privateKey); case SOLANA: return solanaService.mintNFT(nft, privateKey); default: throw new UnsupportedOperationException( "Unsupported blockchain: " + nft.getCollection().getBlockchain()); } } public CompletableFuture<Boolean> verifyOwnership(String contractAddress, BigInteger tokenId, String ownerAddress, Blockchain blockchain) { switch (blockchain) { case ETHEREUM: return ethereumService.verifyOwnership(contractAddress, tokenId, ownerAddress); case POLYGON: return polygonService.verifyOwnership(contractAddress, tokenId, ownerAddress); case SOLANA: return solanaService.verifyOwnership(contractAddress, tokenId, ownerAddress); default: throw new UnsupportedOperationException("Unsupported blockchain: " + blockchain); } } } @Service class EthereumService { private final Web3j web3j; public EthereumService(@Value("${ethereum.rpc.url}") String rpcUrl) { this.web3j = Web3j.build(new HttpService(rpcUrl)); } public CompletableFuture<String> mintNFT(NFT nft, String privateKey) { return CompletableFuture.supplyAsync(() -> { try { // Load ERC721 contract ERC721 contract = ERC721.load( nft.getCollection().getContractAddress(), web3j, Credentials.create(privateKey), new DefaultGasProvider() ); // Mint NFT TransactionReceipt receipt = contract.mint( nft.getOwnerAddress(), nft.getTokenId(), nft.getTokenUri() ).send(); nft.setTransactionHash(receipt.getTransactionHash()); nft.setGasUsed(receipt.getGasUsed()); return receipt.getTransactionHash(); } catch (Exception e) { throw new RuntimeException("Failed to mint NFT on Ethereum: " + e.getMessage(), e); } }); } public CompletableFuture<Boolean> verifyOwnership(String contractAddress, BigInteger tokenId, String ownerAddress) { return CompletableFuture.supplyAsync(() -> { try { ERC721 contract = ERC721.load( contractAddress, web3j, new ReadonlyTransactionManager(web3j, contractAddress), new DefaultGasProvider() ); String actualOwner = contract.ownerOf(tokenId).send(); return actualOwner.equalsIgnoreCase(ownerAddress); } catch (Exception e) { throw new RuntimeException("Failed to verify ownership: " + e.getMessage(), e); } }); } } @Service class PolygonService { // Similar implementation to EthereumService but with Polygon RPC private final Web3j web3j; public PolygonService(@Value("${polygon.rpc.url}") String rpcUrl) { this.web3j = Web3j.build(new HttpService(rpcUrl)); } public CompletableFuture<String> mintNFT(NFT nft, String privateKey) { // Implementation similar to EthereumService // ... } public CompletableFuture<Boolean> verifyOwnership(String contractAddress, BigInteger tokenId, String ownerAddress) { // Implementation similar to EthereumService // ... } } @Service class SolanaService { private final OkHttpClient httpClient; private final String rpcUrl; public SolanaService(@Value("${solana.rpc.url}") String rpcUrl) { this.httpClient = new OkHttpClient(); this.rpcUrl = rpcUrl; } public CompletableFuture<String> mintNFT(NFT nft, String privateKey) { return CompletableFuture.supplyAsync(() -> { try { // Solana NFT minting implementation using JSON RPC // This is a simplified version - real implementation would use Solana Java SDK Map<String, Object> request = new HashMap<>(); request.put("jsonrpc", "2.0"); request.put("id", 1); request.put("method", "mintNFT"); Map<String, Object> params = new HashMap<>(); params.put("mintAddress", nft.getCollection().getContractAddress()); params.put("metadataUri", nft.getTokenUri()); params.put("owner", nft.getOwnerAddress()); params.put("privateKey", privateKey); request.put("params", params); String requestBody = new ObjectMapper().writeValueAsString(request); Request httpRequest = new Request.Builder() .url(rpcUrl) .post(RequestBody.create(requestBody, MediaType.get("application/json"))) .build(); try (Response response = httpClient.newCall(httpRequest).execute()) { if (response.isSuccessful()) { String responseBody = response.body().string(); // Parse response to get transaction signature Map<String, Object> result = new ObjectMapper() .readValue(responseBody, Map.class); return (String) result.get("result"); } else { throw new RuntimeException("Solana RPC call failed: " + response.code()); } } } catch (Exception e) { throw new RuntimeException("Failed to mint NFT on Solana: " + e.getMessage(), e); } }); } public CompletableFuture<Boolean> verifyOwnership(String mintAddress, BigInteger tokenId, String ownerAddress) { // Solana ownership verification implementation return CompletableFuture.completedFuture(true); // Simplified } }

6. NFT Minting Service

import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.concurrent.CompletableFuture; @Service public class NFTMintingService { private final NFTRepository nftRepository; private final CollectionRepository collectionRepository; private final IPFSService ipfsService; private final BlockchainService blockchainService; private final ObjectMapper objectMapper; public NFTMintingService(NFTRepository nftRepository, CollectionRepository collectionRepository, IPFSService ipfsService, BlockchainService blockchainService, ObjectMapper objectMapper) { this.nftRepository = nftRepository; this.collectionRepository = collectionRepository; this.ipfsService = ipfsService; this.blockchainService = blockchainService; this.objectMapper = objectMapper; } @Transactional public CompletableFuture<NFT> mintNFT(NFTMintRequest request, String privateKey) { return CompletableFuture.supplyAsync(() -> { try { // Find collection NFTCollection collection = collectionRepository.findById(request.getCollectionId()) .orElseThrow(() -> new RuntimeException("Collection not found")); // Create NFT entity NFT nft = new NFT(); nft.setName(request.getName()); nft.setDescription(request.getDescription()); nft.setTokenId(request.getTokenId()); nft.setCollection(collection); nft.setCreatorAddress(request.getCreatorAddress()); nft.setOwnerAddress(request.getOwnerAddress() != null ? request.getOwnerAddress() : request.getCreatorAddress()); nft.setStatus(NFTStatus.PENDING); // Convert attributes List<NFTAttribute> attributes = request.getAttributes().stream() .map(attr -> { NFTAttribute nftAttr = new NFTAttribute(); nftAttr.setTraitType(attr.getTraitType()); nftAttr.setValue(attr.getValue()); nftAttr.setDisplayType(attr.getDisplayType()); return nftAttr; }) .collect(Collectors.toList()); nft.setAttributes(attributes); // Save initial NFT record nft = nftRepository.save(nft); // Create and upload metadata to IPFS NFTMetadata metadata = new NFTMetadata( nft.getName(), nft.getDescription(), "ipfs://" + nft.getIpfsHash(), // Image would be uploaded separately nft.getAttributes() ); String metadataHash = ipfsService.uploadMetadata(metadata); nft.setIpfsHash(metadataHash); nft.setTokenUri("ipfs://" + metadataHash); // Update NFT with IPFS hash nft = nftRepository.save(nft); // Mint on blockchain nft.setStatus(NFTStatus.MINTING); nft = nftRepository.save(nft); CompletableFuture<String> mintFuture = blockchainService.mintNFT(nft, privateKey); return mintFuture.thenApply(transactionHash -> { nft.setTransactionHash(transactionHash); nft.setStatus(NFTStatus.MINTED); nft.setMintedAt(LocalDateTime.now()); return nftRepository.save(nft); }).join(); } catch (Exception e) { // Update NFT status to failed try { NFT failedNft = nftRepository.findById(nft.getId()).orElse(nft); failedNft.setStatus(NFTStatus.FAILED); nftRepository.save(failedNft); } catch (Exception ex) { // Log error but don't throw System.err.println("Failed to update NFT status: " + ex.getMessage()); } throw new RuntimeException("NFT minting failed: " + e.getMessage(), e); } }); } @Transactional public CompletableFuture<NFT> batchMint(List<NFTMintRequest> requests, String privateKey) { List<CompletableFuture<NFT>> futures = requests.stream() .map(request -> mintNFT(request, privateKey)) .collect(Collectors.toList()); return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .thenApply(v -> futures.stream() .map(CompletableFuture::join) .collect(Collectors.toList())) .thenApply(list -> list.get(0)); // Return first NFT for response } public CompletableFuture<Boolean> verifyNFT(Long nftId) { return CompletableFuture.supplyAsync(() -> { try { NFT nft = nftRepository.findById(nftId) .orElseThrow(() -> new RuntimeException("NFT not found")); return blockchainService.verifyOwnership( nft.getCollection().getContractAddress(), nft.getTokenId(), nft.getOwnerAddress(), nft.getCollection().getBlockchain() ).join(); } catch (Exception e) { throw new RuntimeException("NFT verification failed: " + e.getMessage(), e); } }); } }

7. REST Controller

import org.springframework.web.bind.annotation.*; import org.springframework.http.ResponseEntity; import org.springframework.http.HttpStatus; import javax.validation.Valid; import java.util.concurrent.CompletableFuture; @RestController @RequestMapping("/api/v1/nft") public class NFTController { private final NFTMintingService nftMintingService; private final NFTRepository nftRepository; private final CollectionRepository collectionRepository; public NFTController(NFTMintingService nftMintingService, NFTRepository nftRepository, CollectionRepository collectionRepository) { this.nftMintingService = nftMintingService; this.nftRepository = nftRepository; this.collectionRepository = collectionRepository; } @PostMapping("/mint") public CompletableFuture<ResponseEntity<ApiResponse<NFTResponse>>> mintNFT( @Valid @RequestBody NFTMintRequest request, @RequestHeader("X-Private-Key") String privateKey) { return nftMintingService.mintNFT(request, privateKey) .thenApply(nft -> ResponseEntity.status(HttpStatus.CREATED) .body(new ApiResponse<>("NFT minting initiated", new NFTResponse(nft)))) .exceptionally(ex -> ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ApiResponse<>(ex.getMessage(), null))); } @PostMapping("/batch-mint") public CompletableFuture<ResponseEntity<ApiResponse<List<NFTResponse>>>> batchMint( @Valid @RequestBody List<NFTMintRequest> requests, @RequestHeader("X-Private-Key") String privateKey) { return nftMintingService.batchMint(requests, privateKey) .thenApply(nft -> { List<NFTResponse> responses = requests.stream() .map(req -> { // In a real implementation, you'd fetch the actual minted NFTs NFTResponse response = new NFTResponse(); response.setName(req.getName()); response.setStatus("MINTING"); return response; }) .collect(Collectors.toList()); return ResponseEntity.status(HttpStatus.CREATED) .body(new ApiResponse<>("Batch minting initiated", responses)); }) .exceptionally(ex -> ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ApiResponse<>(ex.getMessage(), null))); } @GetMapping("/{id}") public ResponseEntity<ApiResponse<NFTResponse>> getNFT(@PathVariable Long id) { return nftRepository.findById(id) .map(nft -> ResponseEntity.ok( new ApiResponse<>("NFT retrieved successfully", new NFTResponse(nft)))) .orElse(ResponseEntity.status(HttpStatus.NOT_FOUND) .body(new ApiResponse<>("NFT not found", null))); } @GetMapping("/collection/{collectionId}") public ResponseEntity<ApiResponse<List<NFTResponse>>> getNFTsByCollection( @PathVariable Long collectionId) { List<NFTResponse> nfts = nftRepository.findByCollectionId(collectionId) .stream() .map(NFTResponse::new) .collect(Collectors.toList()); return ResponseEntity.ok( new ApiResponse<>("NFTs retrieved successfully", nfts)); } @GetMapping("/{id}/verify") public CompletableFuture<ResponseEntity<ApiResponse<Boolean>>> verifyNFT( @PathVariable Long id) { return nftMintingService.verifyNFT(id) .thenApply(verified -> ResponseEntity.ok( new ApiResponse<>("Verification completed", verified))) .exceptionally(ex -> ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ApiResponse<>(ex.getMessage(), false))); } @PostMapping("/collection") public ResponseEntity<ApiResponse<NFTCollection>> createCollection( @Valid @RequestBody CollectionRequest request) { NFTCollection collection = new NFTCollection(); collection.setName(request.getName()); collection.setDescription(request.getDescription()); collection.setSymbol(request.getSymbol()); collection.setBlockchain(request.getBlockchain()); collection.setContractAddress(request.getContractAddress()); collection.setOwnerAddress(request.getOwnerAddress()); collection = collectionRepository.save(collection); return ResponseEntity.status(HttpStatus.CREATED) .body(new ApiResponse<>("Collection created successfully", collection)); } } class ApiResponse<T> { private String message; private T data; private LocalDateTime timestamp; public ApiResponse(String message, T data) { this.message = message; this.data = data; this.timestamp = LocalDateTime.now(); } // Getters and setters public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } public LocalDateTime getTimestamp() { return timestamp; } public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; } }

8. Security Configuration

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(authz -> authz .requestMatchers("/api/v1/nft/mint").authenticated() .requestMatchers("/api/v1/nft/batch-mint").authenticated() .requestMatchers("/api/v1/nft/collection").authenticated() .anyRequest().permitAll() ) .httpBasic(withDefaults()); return http.build(); } }

9. Application Configuration

# application.yml spring: datasource: url: jdbc:postgresql://localhost:5432/nftdb username: nftuser password: nftpassword jpa: hibernate: ddl-auto: update properties: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect ipfs: host: localhost port: 5001 ethereum: rpc: url: https://mainnet.infura.io/v3/your-project-id polygon: rpc: url: https://polygon-rpc.com solana: rpc: url: https://api.mainnet-beta.solana.com server: port: 8080 logging: level: com.yourpackage: DEBUG

Key Features

  1. Multi-Blockchain Support: Ethereum, Polygon, and Solana integration
  2. IPFS Metadata Storage: Secure and decentralized metadata storage
  3. Batch Minting: Efficient multiple NFT minting
  4. Ownership Verification: Real-time NFT ownership verification
  5. RESTful API: Comprehensive API for NFT operations
  6. Security: Basic authentication and secure key handling
  7. Database Persistence: PostgreSQL for reliable data storage
  8. Asynchronous Processing: Non-blocking NFT minting operations

This implementation provides a robust foundation for building an NFT minting API in Java, with extensibility for additional blockchains and features.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper