google-site-verification: google61fe8ba583a51912.html
Ethereum Smart Contract Integration in Java: Complete Guide

Learn how to interact with Ethereum smart contracts from Java applications using Web3j for DeFi, NFTs, and blockchain applications.

Table of Contents

  1. Architecture Overview
  2. Setup & Dependencies
  3. Smart Contract Integration
  4. DeFi Protocol Examples
  5. NFT & ERC721 Integration
  6. Event Monitoring
  7. Security & Best Practices

Architecture Overview

┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ Java App │───▶│ Web3j Library │───▶│ Ethereum Node │ │ │ │ │ │ │ │ - Contract │ │ - ABIs │ │ - JSON-RPC │ │ Clients │ │ - Wrappers │ │ - WebSocket │ │ - Event │ │ - Codegen │ │ - IPC │ │ Listeners │ │ - Filters │ │ │ │ - Transaction │ │ - Transaction │ └─────────────────┘ │ Managers │ │ Management │ │ └─────────────────┘ └──────────────────┘ │ ▼ ┌─────────────────┐ │ Smart Contract │ │ on Ethereum │ └─────────────────┘

Setup & Dependencies

Maven Configuration

<properties> <web3j.version>4.9.7</web3j.version> <okhttp.version>4.10.0</okhttp.version> <jackson.version>2.15.2</jackson.version> </properties> <dependencies> <!-- Web3j Core --> <dependency> <groupId>org.web3j</groupId> <artifactId>core</artifactId> <version>${web3j.version}</version> </dependency> <!-- Web3j Contracts --> <dependency> <groupId>org.web3j</groupId> <artifactId>contracts</artifactId> <version>${web3j.version}</version> </dependency> <!-- HTTP Client --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>${okhttp.version}</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.7</version> </dependency> <!-- Testing --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> </dependencies>

Gradle Configuration

dependencies { implementation 'org.web3j:core:4.9.7' implementation 'org.web3j:contracts:4.9.7' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' }

Core Ethereum Service

1. Web3j Configuration Service

@Service public class Web3jService { private static final Logger logger = LoggerFactory.getLogger(Web3jService.class); private final Web3j web3j; private final TransactionManager transactionManager; private final Credentials credentials; private final GasProvider gasProvider; public Web3jService(@Value("${ethereum.node.url}") String nodeUrl, @Value("${ethereum.wallet.private-key}") String privateKey) { this.web3j = buildWeb3jClient(nodeUrl); this.credentials = Credentials.create(privateKey); this.transactionManager = new RawTransactionManager(web3j, credentials); this.gasProvider = new DefaultGasProvider(); logger.info("Web3j service initialized for network: {}", nodeUrl); } private Web3j buildWeb3jClient(String nodeUrl) { OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .build(); return Web3j.build(new HttpService(nodeUrl, okHttpClient)); } public Web3j getWeb3j() { return web3j; } public Credentials getCredentials() { return credentials; } public TransactionManager getTransactionManager() { return transactionManager; } public GasProvider getGasProvider() { return gasProvider; } public String getCurrentAddress() { return credentials.getAddress(); } public EthBlock.Block getLatestBlock() throws Exception { return web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false) .send() .getBlock(); } public BigInteger getBalance(String address) throws Exception { return web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST) .send() .getBalance(); } public BigInteger getGasPrice() throws Exception { return web3j.ethGasPrice().send().getGasPrice(); } @PreDestroy public void shutdown() { if (web3j != null) { web3j.shutdown(); logger.info("Web3j service shutdown completed"); } } }

2. Contract Loader Service

@Service public class ContractLoaderService { private static final Logger logger = LoggerFactory.getLogger(ContractLoaderService.class); private final Web3jService web3jService; private final Map<String, String> contractABIs = new ConcurrentHashMap<>(); private final Map<String, String> contractAddresses = new ConcurrentHashMap<>(); public ContractLoaderService(Web3jService web3jService) { this.web3jService = web3jService; loadDefaultContracts(); } /** * Load contract from ABI string and address */ public <T extends Contract> T loadContract(String contractName, String contractAddress, Class<T> contractClass) { try { String abi = contractABIs.get(contractName); if (abi == null) { throw new ContractNotFoundException("ABI not found for contract: " + contractName); } return Contract.load( contractAddress, web3jService.getWeb3j(), web3jService.getTransactionManager(), web3jService.getGasProvider(), abi ); } catch (Exception e) { logger.error("Failed to load contract: {}", contractName, e); throw new ContractLoadException("Failed to load contract: " + contractName, e); } } /** * Deploy new contract */ public <T extends Contract> String deployContract(String contractName, Class<T> contractClass, Object... constructorParams) { try { String abi = contractABIs.get(contractName); if (abi == null) { throw new ContractNotFoundException("ABI not found for contract: " + contractName); } String bin = getContractBinary(contractName); // Deploy contract T contract = Contract.deploy( contractClass, web3jService.getWeb3j(), web3jService.getTransactionManager(), web3jService.getGasProvider(), bin, abi, constructorParams ).send(); String contractAddress = contract.getContractAddress(); contractAddresses.put(contractName, contractAddress); logger.info("Deployed contract {} at address: {}", contractName, contractAddress); return contractAddress; } catch (Exception e) { logger.error("Failed to deploy contract: {}", contractName, e); throw new ContractDeploymentException("Failed to deploy contract: " + contractName, e); } } /** * Register contract ABI */ public void registerContract(String contractName, String abi, String address) { contractABIs.put(contractName, abi); if (address != null) { contractAddresses.put(contractName, address); } logger.debug("Registered contract: {}", contractName); } private void loadDefaultContracts() { // ERC20 Standard ABI registerContract("ERC20", ERC20_ABI, null); registerContract("ERC721", ERC721_ABI, null); registerContract("UniswapV2Router", UNISWAP_V2_ROUTER_ABI, null); registerContract("WETH", WETH_ABI, null); } private String getContractBinary(String contractName) { // Load contract binary from resources // This would typically be loaded from compiled .bin files return ""; // Implementation specific } // Standard Contract ABIs private static final String ERC20_ABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"type\":\"function\"},...]"; private static final String ERC721_ABI = "[...]"; private static final String UNISWAP_V2_ROUTER_ABI = "[...]"; private static final String WETH_ABI = "[...]"; }

Smart Contract Integration

1. ERC20 Token Service

@Service public class ERC20Service { private static final Logger logger = LoggerFactory.getLogger(ERC20Service.class); private final Web3jService web3jService; private final ContractLoaderService contractLoader; public ERC20Service(Web3jService web3jService, ContractLoaderService contractLoader) { this.web3jService = web3jService; this.contractLoader = contractLoader; } /** * Get ERC20 token balance */ public BigInteger getBalance(String tokenAddress, String walletAddress) { try { ERC20 token = loadERC20(tokenAddress); return token.balanceOf(walletAddress).send(); } catch (Exception e) { logger.error("Failed to get balance for token: {}", tokenAddress, e); throw new ContractCallException("Failed to get token balance", e); } } /** * Transfer ERC20 tokens */ public TransactionReceipt transfer(String tokenAddress, String toAddress, BigInteger amount) { try { ERC20 token = loadERC20(tokenAddress); return token.transfer(toAddress, amount).send(); } catch (Exception e) { logger.error("Failed to transfer tokens from {} to {}", tokenAddress, toAddress, e); throw new ContractCallException("Token transfer failed", e); } } /** * Get token metadata */ public TokenInfo getTokenInfo(String tokenAddress) { try { ERC20 token = loadERC20(tokenAddress); String name = token.name().send(); String symbol = token.symbol().send(); BigInteger decimals = token.decimals().send(); BigInteger totalSupply = token.totalSupply().send(); return TokenInfo.builder() .address(tokenAddress) .name(name) .symbol(symbol) .decimals(decimals.intValue()) .totalSupply(totalSupply) .build(); } catch (Exception e) { logger.error("Failed to get token info: {}", tokenAddress, e); throw new ContractCallException("Failed to get token info", e); } } /** * Get token allowance */ public BigInteger getAllowance(String tokenAddress, String owner, String spender) { try { ERC20 token = loadERC20(tokenAddress); return token.allowance(owner, spender).send(); } catch (Exception e) { logger.error("Failed to get allowance for token: {}", tokenAddress, e); throw new ContractCallException("Failed to get allowance", e); } } /** * Approve token spending */ public TransactionReceipt approve(String tokenAddress, String spender, BigInteger amount) { try { ERC20 token = loadERC20(tokenAddress); return token.approve(spender, amount).send(); } catch (Exception e) { logger.error("Failed to approve spending for token: {}", tokenAddress, e); throw new ContractCallException("Token approval failed", e); } } private ERC20 loadERC20(String tokenAddress) { return contractLoader.loadContract("ERC20", tokenAddress, ERC20.class); } @Data @Builder public static class TokenInfo { private String address; private String name; private String symbol; private int decimals; private BigInteger totalSupply; } }

2. DeFi Service (Uniswap Integration)

@Service public class UniswapService { private static final Logger logger = LoggerFactory.getLogger(UniswapService.class); private final Web3jService web3jService; private final ContractLoaderService contractLoader; private final ERC20Service erc20Service; // Mainnet Uniswap V2 Router address private static final String UNISWAP_V2_ROUTER = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"; // WETH address private static final String WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; public UniswapService(Web3jService web3jService, ContractLoaderService contractLoader, ERC20Service erc20Service) { this.web3jService = web3jService; this.contractLoader = contractLoader; this.erc20Service = erc20Service; } /** * Swap exact ETH for tokens */ public TransactionReceipt swapExactETHForTokens( String tokenOut, BigInteger amountOutMin, BigInteger valueETH) { try { // Get router contract UniswapV2Router02 router = loadRouter(); // Define path (ETH -> WETH -> Token) List<String> path = Arrays.asList(WETH, tokenOut); // Define deadline (20 minutes from now) BigInteger deadline = BigInteger.valueOf( System.currentTimeMillis() / 1000 + 20 * 60 ); // Execute swap return router.swapExactETHForTokens( amountOutMin, path, web3jService.getCurrentAddress(), deadline ).send(); } catch (Exception e) { logger.error("Failed to swap ETH for tokens", e); throw new ContractCallException("Swap failed", e); } } /** * Swap exact tokens for ETH */ public TransactionReceipt swapExactTokensForETH( String tokenIn, BigInteger amountIn, BigInteger amountOutMin) { try { UniswapV2Router02 router = loadRouter(); // Approve router to spend tokens erc20Service.approve(tokenIn, UNISWAP_V2_ROUTER, amountIn); // Define path (Token -> WETH -> ETH) List<String> path = Arrays.asList(tokenIn, WETH); // Define deadline BigInteger deadline = BigInteger.valueOf( System.currentTimeMillis() / 1000 + 20 * 60 ); // Execute swap return router.swapExactTokensForETH( amountIn, amountOutMin, path, web3jService.getCurrentAddress(), deadline ).send(); } catch (Exception e) { logger.error("Failed to swap tokens for ETH", e); throw new ContractCallException("Swap failed", e); } } /** * Get price quote */ public List<BigInteger> getAmountsOut(BigInteger amountIn, List<String> path) { try { UniswapV2Router02 router = loadRouter(); return router.getAmountsOut(amountIn, path).send(); } catch (Exception e) { logger.error("Failed to get price quote", e); throw new ContractCallException("Price quote failed", e); } } /** * Add liquidity */ public TransactionReceipt addLiquidity( String tokenA, String tokenB, BigInteger amountADesired, BigInteger amountBDesired, BigInteger amountAMin, BigInteger amountBMin) { try { UniswapV2Router02 router = loadRouter(); // Approve tokens erc20Service.approve(tokenA, UNISWAP_V2_ROUTER, amountADesired); erc20Service.approve(tokenB, UNISWAP_V2_ROUTER, amountBDesired); // Define deadline BigInteger deadline = BigInteger.valueOf( System.currentTimeMillis() / 1000 + 20 * 60 ); // Add liquidity return router.addLiquidity( tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin, web3jService.getCurrentAddress(), deadline ).send(); } catch (Exception e) { logger.error("Failed to add liquidity", e); throw new ContractCallException("Add liquidity failed", e); } } private UniswapV2Router02 loadRouter() { return contractLoader.loadContract( "UniswapV2Router", UNISWAP_V2_ROUTER, UniswapV2Router02.class ); } }

3. NFT Service (ERC721)

@Service public class NFTService { private static final Logger logger = LoggerFactory.getLogger(NFTService.class); private final Web3jService web3jService; private final ContractLoaderService contractLoader; public NFTService(Web3jService web3jService, ContractLoaderService contractLoader) { this.web3jService = web3jService; this.contractLoader = contractLoader; } /** * Get NFT balance of address */ public BigInteger getBalance(String contractAddress, String owner) { try { ERC721 nft = loadERC721(contractAddress); return nft.balanceOf(owner).send(); } catch (Exception e) { logger.error("Failed to get NFT balance for contract: {}", contractAddress, e); throw new ContractCallException("Failed to get NFT balance", e); } } /** * Get NFT owner */ public String getOwnerOf(String contractAddress, BigInteger tokenId) { try { ERC721 nft = loadERC721(contractAddress); return nft.ownerOf(tokenId).send(); } catch (Exception e) { logger.error("Failed to get owner for token {}: {}", tokenId, contractAddress, e); throw new ContractCallException("Failed to get NFT owner", e); } } /** * Get NFT metadata URI */ public String getTokenURI(String contractAddress, BigInteger tokenId) { try { ERC721 nft = loadERC721(contractAddress); return nft.tokenURI(tokenId).send(); } catch (Exception e) { logger.error("Failed to get token URI for token {}: {}", tokenId, contractAddress, e); throw new ContractCallException("Failed to get token URI", e); } } /** * Transfer NFT */ public TransactionReceipt transferFrom(String contractAddress, String from, String to, BigInteger tokenId) { try { ERC721 nft = loadERC721(contractAddress); return nft.transferFrom(from, to, tokenId).send(); } catch (Exception e) { logger.error("Failed to transfer NFT {} from {} to {}", tokenId, from, to, e); throw new ContractCallException("NFT transfer failed", e); } } /** * Safe transfer NFT */ public TransactionReceipt safeTransferFrom(String contractAddress, String from, String to, BigInteger tokenId) { try { ERC721 nft = loadERC721(contractAddress); return nft.safeTransferFrom(from, to, tokenId).send(); } catch (Exception e) { logger.error("Failed to safe transfer NFT {} from {} to {}", tokenId, from, to, e); throw new ContractCallException("NFT safe transfer failed", e); } } /** * Check NFT approval */ public String getApproved(String contractAddress, BigInteger tokenId) { try { ERC721 nft = loadERC721(contractAddress); return nft.getApproved(tokenId).send(); } catch (Exception e) { logger.error("Failed to get approved address for token {}: {}", tokenId, contractAddress, e); throw new ContractCallException("Failed to get approved address", e); } } /** * Set approval for all */ public TransactionReceipt setApprovalForAll(String contractAddress, String operator, boolean approved) { try { ERC721 nft = loadERC721(contractAddress); return nft.setApprovalForAll(operator, approved).send(); } catch (Exception e) { logger.error("Failed to set approval for all for operator: {}", operator, e); throw new ContractCallException("Set approval for all failed", e); } } private ERC721 loadERC721(String contractAddress) { return contractLoader.loadContract("ERC721", contractAddress, ERC721.class); } }

Event Monitoring

1. Event Listener Service

@Service public class EventListenerService { private static final Logger logger = LoggerFactory.getLogger(EventListenerService.class); private final Web3jService web3jService; private final Map<String, Disposable> eventSubscriptions = new ConcurrentHashMap<>(); public EventListenerService(Web3jService web3jService) { this.web3jService = web3jService; } /** * Listen for ERC20 Transfer events */ public void listenToERC20Transfers(String tokenAddress, Consumer<ERC20.TransferEventResponse> handler) { try { ERC20 token = ERC20.load( tokenAddress, web3jService.getWeb3j(), web3jService.getTransactionManager(), web3jService.getGasProvider() ); Disposable subscription = token.transferEventFlowable( DefaultBlockParameterName.EARLIEST, DefaultBlockParameterName.LATEST ).subscribe(event -> { logger.info("ERC20 Transfer: from {} to {} amount {}", event.from, event.to, event.value); handler.accept(event); }); eventSubscriptions.put("ERC20_" + tokenAddress, subscription); logger.info("Started listening to ERC20 transfers for: {}", tokenAddress); } catch (Exception e) { logger.error("Failed to listen to ERC20 transfers: {}", tokenAddress, e); throw new EventListenerException("Failed to listen to ERC20 transfers", e); } } /** * Listen for new blocks */ public void listenToNewBlocks(Consumer<EthBlock.Block> handler) { Disposable subscription = web3jService.getWeb3j() .blockFlowable(true) .subscribe(ethBlock -> { EthBlock.Block block = ethBlock.getBlock(); logger.debug("New block: {}", block.getNumber()); handler.accept(block); }); eventSubscriptions.put("NEW_BLOCKS", subscription); logger.info("Started listening to new blocks"); } /** * Listen for pending transactions */ public void listenToPendingTransactions(Consumer<String> handler) { Disposable subscription = web3jService.getWeb3j() .pendingTransactionFlowable() .subscribe(transaction -> { logger.debug("Pending transaction: {}", transaction.getHash()); handler.accept(transaction.getHash()); }); eventSubscriptions.put("PENDING_TXS", subscription); logger.info("Started listening to pending transactions"); } /** * Stop event listener */ public void stopListener(String listenerId) { Disposable subscription = eventSubscriptions.get(listenerId); if (subscription != null && !subscription.isDisposed()) { subscription.dispose(); eventSubscriptions.remove(listenerId); logger.info("Stopped listener: {}", listenerId); } } /** * Stop all listeners */ @PreDestroy public void stopAllListeners() { eventSubscriptions.forEach((id, subscription) -> { if (!subscription.isDisposed()) { subscription.dispose(); } }); eventSubscriptions.clear(); logger.info("Stopped all event listeners"); } }

2. Transaction Monitoring Service

@Service public class TransactionMonitorService { private static final Logger logger = LoggerFactory.getLogger(TransactionMonitorService.class); private final Web3jService web3jService; private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); public TransactionMonitorService(Web3jService web3jService) { this.web3jService = web3jService; } /** * Wait for transaction receipt with timeout */ public TransactionReceipt waitForTransactionReceipt(String transactionHash, long timeoutSeconds) { try { Optional<TransactionReceipt> receipt = web3jService.getWeb3j() .ethGetTransactionReceipt(transactionHash) .send() .getTransactionReceipt(); if (receipt.isPresent()) { return receipt.get(); } // Poll for receipt long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() - startTime < timeoutSeconds * 1000) { Thread.sleep(2000); // Wait 2 seconds between checks receipt = web3jService.getWeb3j() .ethGetTransactionReceipt(transactionHash) .send() .getTransactionReceipt(); if (receipt.isPresent()) { return receipt.get(); } logger.debug("Waiting for transaction receipt: {}", transactionHash); } throw new TransactionTimeoutException( "Transaction receipt not received within timeout: " + transactionHash); } catch (Exception e) { logger.error("Failed to get transaction receipt: {}", transactionHash, e); throw new TransactionMonitorException("Failed to monitor transaction", e); } } /** * Check transaction status */ public TransactionStatus getTransactionStatus(String transactionHash) { try { Optional<TransactionReceipt> receipt = web3jService.getWeb3j() .ethGetTransactionReceipt(transactionHash) .send() .getTransactionReceipt(); if (receipt.isPresent()) { TransactionReceipt txReceipt = receipt.get(); return TransactionStatus.builder() .hash(transactionHash) .status(txReceipt.isStatusOK() ? "SUCCESS" : "FAILED") .blockHash(txReceipt.getBlockHash()) .blockNumber(txReceipt.getBlockNumber()) .gasUsed(txReceipt.getGasUsed()) .build(); } else { return TransactionStatus.builder() .hash(transactionHash) .status("PENDING") .build(); } } catch (Exception e) { logger.error("Failed to get transaction status: {}", transactionHash, e); throw new TransactionMonitorException("Failed to get transaction status", e); } } @Data @Builder public static class TransactionStatus { private String hash; private String status; // PENDING, SUCCESS, FAILED private String blockHash; private BigInteger blockNumber; private BigInteger gasUsed; } }

Configuration & Properties

application.yml

# Ethereum Configuration ethereum: node: url: ${ETHEREUM_NODE_URL:https://mainnet.infura.io/v3/your-project-id} network: ${ETHEREUM_NETWORK:mainnet} wallet: private-key: ${WALLET_PRIVATE_KEY:} address: ${WALLET_ADDRESS:} contracts: uniswap-router: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" gas: multiplier: 1.1 priority: "fast" # Application Settings app: transaction: timeout-seconds: 180 confirmation-blocks: 12 monitoring: enabled: true block-interval-ms: 15000 # Logging logging: level: org.web3j: INFO com.yourcompany.ethereum: DEBUG

Configuration Class

@Configuration @ConfigurationProperties(prefix = "ethereum") @Data public class EthereumConfig { private Node node = new Node(); private Wallet wallet = new Wallet(); private Contracts contracts = new Contracts(); private Gas gas = new Gas(); @Data public static class Node { private String url; private String network; } @Data public static class Wallet { private String privateKey; private String address; } @Data public static class Contracts { private String uniswapRouter; private String weth; } @Data public static class Gas { private double multiplier; private String priority; } }

Security & Best Practices

1. Secure Credential Management

@Service public class SecureCredentialService { private static final Logger logger = LoggerFactory.getLogger(SecureCredentialService.class); /** * Load private key from secure source */ public Credentials loadCredentials() { try { String privateKey = getPrivateKeyFromSecureSource(); if (privateKey == null || privateKey.trim().isEmpty()) { throw new SecurityException("Private key not found"); } // Validate private key format if (!privateKey.startsWith("0x")) { privateKey = "0x" + privateKey; } if (privateKey.length() != 66) { // 32 bytes + 0x prefix throw new SecurityException("Invalid private key length"); } return Credentials.create(privateKey); } catch (Exception e) { logger.error("Failed to load credentials", e); throw new SecurityException("Credential loading failed", e); } } private String getPrivateKeyFromSecureSource() { // Priority order for private key sources return Optional.ofNullable(System.getenv("WALLET_PRIVATE_KEY")) .or(() -> Optional.ofNullable(System.getProperty("wallet.private-key"))) .orElseThrow(() -> new SecurityException("No private key source found")); } /** * Validate address format */ public boolean isValidAddress(String address) { if (address == null || !address.startsWith("0x") || address.length() != 42) { return false; } try { // Basic hex validation return address.matches("^0x[0-9a-fA-F]{40}$"); } catch (Exception e) { return false; } } }

2. Gas Optimization Service

@Service public class GasOptimizationService { private final Web3jService web3jService; public GasOptimizationService(Web3jService web3jService) { this.web3jService = web3jService; } /** * Calculate optimal gas price based on network conditions */ public BigInteger calculateOptimalGasPrice() { try { // Get current gas price from network BigInteger currentGasPrice = web3jService.getGasPrice(); // Apply multiplier based on priority double multiplier = getGasMultiplier(); BigInteger optimalGasPrice = currentGasPrice .multiply(BigInteger.valueOf((long) (multiplier * 100))) .divide(BigInteger.valueOf(100)); logger.debug("Optimal gas price calculated: {} (base: {})", optimalGasPrice, currentGasPrice); return optimalGasPrice; } catch (Exception e) { logger.warn("Failed to calculate optimal gas price, using default", e); return new DefaultGasProvider().getGasPrice(); } } /** * Estimate gas limit for transaction */ public BigInteger estimateGasLimit(Transaction transaction) { try { // This would typically call eth_estimateGas // For simplicity, returning a safe default return BigInteger.valueOf(21000); // Base gas limit } catch (Exception e) { logger.warn("Failed to estimate gas limit, using default", e); return new DefaultGasProvider().getGasLimit(); } } private double getGasMultiplier() { // Could be configured based on transaction priority return 1.1; // 10% above current gas price } }

Error Handling & Exceptions

public class EthereumException extends RuntimeException { public EthereumException(String message) { super(message); } public EthereumException(String message, Throwable cause) { super(message, cause); } } public class ContractCallException extends EthereumException { public ContractCallException(String message) { super(message); } public ContractCallException(String message, Throwable cause) { super(message, cause); } } public class TransactionTimeoutException extends EthereumException { public TransactionTimeoutException(String message) { super(message); } } public class InsufficientFundsException extends EthereumException { public InsufficientFundsException(String message) { super(message); } } @ControllerAdvice public class EthereumExceptionHandler { @ExceptionHandler(ContractCallException.class) public ResponseEntity<ErrorResponse> handleContractCallException(ContractCallException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(ErrorResponse.builder() .error("CONTRACT_CALL_ERROR") .message(e.getMessage()) .timestamp(Instant.now()) .build()); } @ExceptionHandler(TransactionTimeoutException.class) public ResponseEntity<ErrorResponse> handleTransactionTimeout(TransactionTimeoutException e) { return ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT) .body(ErrorResponse.builder() .error("TRANSACTION_TIMEOUT") .message(e.getMessage()) .timestamp(Instant.now()) .build()); } @ExceptionHandler(InsufficientFundsException.class) public ResponseEntity<ErrorResponse> handleInsufficientFunds(InsufficientFundsException e) { return ResponseEntity.status(HttpStatus.PAYMENT_REQUIRED) .body(ErrorResponse.builder() .error("INSUFFICIENT_FUNDS") .message(e.getMessage()) .timestamp(Instant.now()) .build()); } @Data @Builder public static class ErrorResponse { private String error; private String message; private Instant timestamp; } }

This comprehensive implementation provides a robust foundation for interacting with Ethereum smart contracts from Java applications, covering everything from basic token operations to advanced DeFi protocols and NFT management.

Leave a Reply

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


Macro Nepal Helper