JavaScript Blockchain

JavaScript Blockchain Development: Web3, Smart Contracts, and DApp Development

Build blockchain applications with JavaScript. Learn Web3.js, Ethereum development, smart contract interaction, and decentralized app creation.

By JavaScript Document Team
blockchainweb3ethereumsmart-contractsdappcryptocurrency

Blockchain development with JavaScript enables building decentralized applications (DApps) that interact with blockchain networks like Ethereum. Using libraries like Web3.js and Ethers.js, developers can create applications that interact with smart contracts, handle cryptocurrency transactions, and build complete Web3 experiences. This comprehensive guide covers blockchain fundamentals, smart contract interaction, and DApp development.

Blockchain Fundamentals and Web3 Setup

Web3.js Integration

// Web3 Connection Manager
class Web3Manager {
  constructor() {
    this.web3 = null;
    this.account = null;
    this.networkId = null;
    this.isConnected = false;
    this.provider = null;
    this.contracts = new Map();
    this.eventListeners = new Map();
  }

  // Initialize Web3 connection
  async initialize(providerUrl = null) {
    try {
      // Check if MetaMask is available
      if (typeof window !== 'undefined' && window.ethereum) {
        this.provider = window.ethereum;
        this.web3 = new Web3(window.ethereum);

        // Request account access
        await this.connectWallet();
      } else if (providerUrl) {
        // Use provided RPC URL
        this.provider = new Web3.providers.HttpProvider(providerUrl);
        this.web3 = new Web3(this.provider);
      } else {
        throw new Error('No Web3 provider available');
      }

      // Get network information
      this.networkId = await this.web3.eth.net.getId();
      console.log('Connected to network:', this.networkId);

      this.isConnected = true;
      this.setupEventListeners();

      return this;
    } catch (error) {
      console.error('Web3 initialization failed:', error);
      throw error;
    }
  }

  // Connect to MetaMask wallet
  async connectWallet() {
    try {
      const accounts = await window.ethereum.request({
        method: 'eth_requestAccounts',
      });

      this.account = accounts[0];
      console.log('Connected account:', this.account);

      return this.account;
    } catch (error) {
      console.error('Wallet connection failed:', error);
      throw error;
    }
  }

  // Switch network
  async switchNetwork(chainId) {
    try {
      await window.ethereum.request({
        method: 'wallet_switchEthereumChain',
        params: [{ chainId: this.web3.utils.toHex(chainId) }],
      });

      this.networkId = chainId;
    } catch (error) {
      // Network doesn't exist, add it
      if (error.code === 4902) {
        await this.addNetwork(chainId);
      } else {
        throw error;
      }
    }
  }

  // Add custom network
  async addNetwork(chainId) {
    const networks = {
      5: {
        chainId: '0x5',
        chainName: 'Goerli Test Network',
        nativeCurrency: {
          name: 'ETH',
          symbol: 'ETH',
          decimals: 18,
        },
        rpcUrls: ['https://goerli.infura.io/v3/YOUR_PROJECT_ID'],
        blockExplorerUrls: ['https://goerli.etherscan.io'],
      },
      137: {
        chainId: '0x89',
        chainName: 'Polygon Mainnet',
        nativeCurrency: {
          name: 'MATIC',
          symbol: 'MATIC',
          decimals: 18,
        },
        rpcUrls: ['https://polygon-rpc.com/'],
        blockExplorerUrls: ['https://polygonscan.com/'],
      },
    };

    const networkConfig = networks[chainId];
    if (!networkConfig) {
      throw new Error('Unsupported network');
    }

    await window.ethereum.request({
      method: 'wallet_addEthereumChain',
      params: [networkConfig],
    });
  }

  // Setup event listeners
  setupEventListeners() {
    if (window.ethereum) {
      // Account change listener
      window.ethereum.on('accountsChanged', (accounts) => {
        this.account = accounts[0] || null;
        this.onAccountChanged(this.account);
      });

      // Network change listener
      window.ethereum.on('chainChanged', (chainId) => {
        this.networkId = parseInt(chainId, 16);
        this.onNetworkChanged(this.networkId);
      });

      // Connection listener
      window.ethereum.on('connect', (connectInfo) => {
        this.isConnected = true;
        this.onConnected(connectInfo);
      });

      // Disconnection listener
      window.ethereum.on('disconnect', (error) => {
        this.isConnected = false;
        this.onDisconnected(error);
      });
    }
  }

  // Event handlers (override in subclasses)
  onAccountChanged(newAccount) {
    console.log('Account changed:', newAccount);
  }

  onNetworkChanged(newNetworkId) {
    console.log('Network changed:', newNetworkId);
  }

  onConnected(connectInfo) {
    console.log('Connected:', connectInfo);
  }

  onDisconnected(error) {
    console.log('Disconnected:', error);
  }

  // Utility methods
  toWei(amount, unit = 'ether') {
    return this.web3.utils.toWei(amount.toString(), unit);
  }

  fromWei(amount, unit = 'ether') {
    return this.web3.utils.fromWei(amount.toString(), unit);
  }

  isValidAddress(address) {
    return this.web3.utils.isAddress(address);
  }

  async getBalance(address = null) {
    const targetAddress = address || this.account;
    if (!targetAddress) throw new Error('No address provided');

    const balance = await this.web3.eth.getBalance(targetAddress);
    return this.fromWei(balance);
  }

  async getGasPrice() {
    return await this.web3.eth.getGasPrice();
  }

  async estimateGas(transaction) {
    return await this.web3.eth.estimateGas(transaction);
  }

  // Transaction methods
  async sendTransaction(to, value, data = '0x', gasLimit = null) {
    const transaction = {
      from: this.account,
      to,
      value: this.toWei(value),
      data,
    };

    if (gasLimit) {
      transaction.gas = gasLimit;
    } else {
      transaction.gas = await this.estimateGas(transaction);
    }

    transaction.gasPrice = await this.getGasPrice();

    return await this.web3.eth.sendTransaction(transaction);
  }

  async getTransactionReceipt(txHash) {
    return await this.web3.eth.getTransactionReceipt(txHash);
  }

  async waitForTransaction(txHash, confirmations = 1) {
    return new Promise((resolve, reject) => {
      const checkTransaction = async () => {
        try {
          const receipt = await this.getTransactionReceipt(txHash);

          if (receipt) {
            const currentBlock = await this.web3.eth.getBlockNumber();
            const confirmedBlocks = currentBlock - receipt.blockNumber;

            if (confirmedBlocks >= confirmations) {
              resolve(receipt);
            } else {
              setTimeout(checkTransaction, 5000); // Check every 5 seconds
            }
          } else {
            setTimeout(checkTransaction, 5000);
          }
        } catch (error) {
          reject(error);
        }
      };

      checkTransaction();
    });
  }
}

// Smart Contract Manager
class SmartContractManager {
  constructor(web3Manager) {
    this.web3Manager = web3Manager;
    this.web3 = web3Manager.web3;
    this.contracts = new Map();
    this.abis = new Map();
  }

  // Load contract ABI
  loadABI(contractName, abi) {
    this.abis.set(contractName, abi);
  }

  // Create contract instance
  createContract(contractName, address, abi = null) {
    const contractABI = abi || this.abis.get(contractName);
    if (!contractABI) {
      throw new Error(`ABI not found for contract: ${contractName}`);
    }

    const contract = new this.web3.eth.Contract(contractABI, address);
    this.contracts.set(contractName, contract);

    return contract;
  }

  // Get contract instance
  getContract(contractName) {
    return this.contracts.get(contractName);
  }

  // Call contract method (read-only)
  async callMethod(contractName, methodName, ...args) {
    const contract = this.getContract(contractName);
    if (!contract) {
      throw new Error(`Contract not found: ${contractName}`);
    }

    return await contract.methods[methodName](...args).call();
  }

  // Send contract transaction (write)
  async sendMethod(contractName, methodName, options = {}, ...args) {
    const contract = this.getContract(contractName);
    if (!contract) {
      throw new Error(`Contract not found: ${contractName}`);
    }

    const method = contract.methods[methodName](...args);

    const transaction = {
      from: this.web3Manager.account,
      to: contract.options.address,
      data: method.encodeABI(),
      value: options.value || 0,
      gas:
        options.gas ||
        (await method.estimateGas({ from: this.web3Manager.account })),
      gasPrice: options.gasPrice || (await this.web3.eth.getGasPrice()),
    };

    return await this.web3.eth.sendTransaction(transaction);
  }

  // Listen to contract events
  subscribeToEvent(contractName, eventName, options = {}) {
    const contract = this.getContract(contractName);
    if (!contract) {
      throw new Error(`Contract not found: ${contractName}`);
    }

    const eventSubscription = contract.events[eventName](options);

    return eventSubscription;
  }

  // Get past events
  async getPastEvents(contractName, eventName, options = {}) {
    const contract = this.getContract(contractName);
    if (!contract) {
      throw new Error(`Contract not found: ${contractName}`);
    }

    return await contract.getPastEvents(eventName, options);
  }

  // Deploy contract
  async deployContract(
    contractName,
    bytecode,
    constructorArgs = [],
    options = {}
  ) {
    const abi = this.abis.get(contractName);
    if (!abi) {
      throw new Error(`ABI not found for contract: ${contractName}`);
    }

    const contract = new this.web3.eth.Contract(abi);

    const deployment = contract.deploy({
      data: bytecode,
      arguments: constructorArgs,
    });

    const gas = options.gas || (await deployment.estimateGas());
    const gasPrice = options.gasPrice || (await this.web3.eth.getGasPrice());

    const deployedContract = await deployment.send({
      from: this.web3Manager.account,
      gas,
      gasPrice,
    });

    this.contracts.set(contractName, deployedContract);

    return deployedContract;
  }
}

// ERC-20 Token Manager
class ERC20TokenManager {
  constructor(contractManager) {
    this.contractManager = contractManager;
    this.web3 = contractManager.web3;

    // Standard ERC-20 ABI
    this.erc20ABI = [
      {
        constant: true,
        inputs: [],
        name: 'name',
        outputs: [{ name: '', type: 'string' }],
        type: 'function',
      },
      {
        constant: true,
        inputs: [],
        name: 'symbol',
        outputs: [{ name: '', type: 'string' }],
        type: 'function',
      },
      {
        constant: true,
        inputs: [],
        name: 'decimals',
        outputs: [{ name: '', type: 'uint8' }],
        type: 'function',
      },
      {
        constant: true,
        inputs: [],
        name: 'totalSupply',
        outputs: [{ name: '', type: 'uint256' }],
        type: 'function',
      },
      {
        constant: true,
        inputs: [{ name: 'owner', type: 'address' }],
        name: 'balanceOf',
        outputs: [{ name: '', type: 'uint256' }],
        type: 'function',
      },
      {
        constant: false,
        inputs: [
          { name: 'to', type: 'address' },
          { name: 'value', type: 'uint256' },
        ],
        name: 'transfer',
        outputs: [{ name: '', type: 'bool' }],
        type: 'function',
      },
      {
        constant: false,
        inputs: [
          { name: 'spender', type: 'address' },
          { name: 'value', type: 'uint256' },
        ],
        name: 'approve',
        outputs: [{ name: '', type: 'bool' }],
        type: 'function',
      },
      {
        constant: true,
        inputs: [
          { name: 'owner', type: 'address' },
          { name: 'spender', type: 'address' },
        ],
        name: 'allowance',
        outputs: [{ name: '', type: 'uint256' }],
        type: 'function',
      },
      {
        anonymous: false,
        inputs: [
          { indexed: true, name: 'from', type: 'address' },
          { indexed: true, name: 'to', type: 'address' },
          { indexed: false, name: 'value', type: 'uint256' },
        ],
        name: 'Transfer',
        type: 'event',
      },
      {
        anonymous: false,
        inputs: [
          { indexed: true, name: 'owner', type: 'address' },
          { indexed: true, name: 'spender', type: 'address' },
          { indexed: false, name: 'value', type: 'uint256' },
        ],
        name: 'Approval',
        type: 'event',
      },
    ];
  }

  // Load ERC-20 token
  loadToken(tokenName, tokenAddress) {
    this.contractManager.loadABI(tokenName, this.erc20ABI);
    return this.contractManager.createContract(tokenName, tokenAddress);
  }

  // Get token information
  async getTokenInfo(tokenName) {
    const name = await this.contractManager.callMethod(tokenName, 'name');
    const symbol = await this.contractManager.callMethod(tokenName, 'symbol');
    const decimals = await this.contractManager.callMethod(
      tokenName,
      'decimals'
    );
    const totalSupply = await this.contractManager.callMethod(
      tokenName,
      'totalSupply'
    );

    return {
      name,
      symbol,
      decimals: parseInt(decimals),
      totalSupply: totalSupply.toString(),
    };
  }

  // Get token balance
  async getBalance(tokenName, address) {
    const balance = await this.contractManager.callMethod(
      tokenName,
      'balanceOf',
      address
    );
    const tokenInfo = await this.getTokenInfo(tokenName);

    return {
      raw: balance.toString(),
      formatted: (balance / Math.pow(10, tokenInfo.decimals)).toString(),
    };
  }

  // Transfer tokens
  async transfer(tokenName, to, amount, decimals = 18) {
    const value = (amount * Math.pow(10, decimals)).toString();
    return await this.contractManager.sendMethod(
      tokenName,
      'transfer',
      {},
      to,
      value
    );
  }

  // Approve token spending
  async approve(tokenName, spender, amount, decimals = 18) {
    const value = (amount * Math.pow(10, decimals)).toString();
    return await this.contractManager.sendMethod(
      tokenName,
      'approve',
      {},
      spender,
      value
    );
  }

  // Get allowance
  async getAllowance(tokenName, owner, spender) {
    const allowance = await this.contractManager.callMethod(
      tokenName,
      'allowance',
      owner,
      spender
    );
    return allowance.toString();
  }

  // Monitor token transfers
  monitorTransfers(tokenName, options = {}) {
    return this.contractManager.subscribeToEvent(
      tokenName,
      'Transfer',
      options
    );
  }
}

// Usage example
async function initializeWeb3App() {
  try {
    // Initialize Web3
    const web3Manager = new Web3Manager();
    await web3Manager.initialize();

    console.log('Web3 initialized');
    console.log('Account:', web3Manager.account);
    console.log('Network ID:', web3Manager.networkId);

    // Get account balance
    const balance = await web3Manager.getBalance();
    console.log('Account balance:', balance, 'ETH');

    // Initialize contract manager
    const contractManager = new SmartContractManager(web3Manager);

    // Initialize ERC-20 token manager
    const tokenManager = new ERC20TokenManager(contractManager);

    // Example: Load USDC token (mainnet address)
    const usdcAddress = '0xA0b86a33E6441B8A4B8A45C0C26e9DFF1c66ec59'; // Example address
    tokenManager.loadToken('USDC', usdcAddress);

    // Get token info
    try {
      const tokenInfo = await tokenManager.getTokenInfo('USDC');
      console.log('Token info:', tokenInfo);

      // Get token balance
      const tokenBalance = await tokenManager.getBalance(
        'USDC',
        web3Manager.account
      );
      console.log('Token balance:', tokenBalance);
    } catch (error) {
      console.log(
        'Token operations failed (expected if not on mainnet):',
        error.message
      );
    }

    return { web3Manager, contractManager, tokenManager };
  } catch (error) {
    console.error('Web3 app initialization failed:', error);
    throw error;
  }
}

// Initialize the app
initializeWeb3App()
  .then((result) => {
    console.log('Web3 app initialized successfully');
  })
  .catch((error) => {
    console.error('Failed to initialize Web3 app:', error);
  });

DeFi Integration and DEX Development

Decentralized Exchange (DEX) Implementation

// DEX Manager for Uniswap-like functionality
class DEXManager {
  constructor(contractManager, tokenManager) {
    this.contractManager = contractManager;
    this.tokenManager = tokenManager;
    this.web3 = contractManager.web3;
    this.pairs = new Map();
    this.reserves = new Map();

    // Uniswap V2 Router ABI (simplified)
    this.routerABI = [
      {
        inputs: [
          { internalType: 'uint256', name: 'amountIn', type: 'uint256' },
          { internalType: 'address[]', name: 'path', type: 'address[]' },
        ],
        name: 'getAmountsOut',
        outputs: [
          { internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' },
        ],
        stateMutability: 'view',
        type: 'function',
      },
      {
        inputs: [
          { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' },
          { internalType: 'address[]', name: 'path', type: 'address[]' },
          { internalType: 'address', name: 'to', type: 'address' },
          { internalType: 'uint256', name: 'deadline', type: 'uint256' },
        ],
        name: 'swapExactETHForTokens',
        outputs: [
          { internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' },
        ],
        stateMutability: 'payable',
        type: 'function',
      },
      {
        inputs: [
          { internalType: 'uint256', name: 'amountIn', type: 'uint256' },
          { internalType: 'uint256', name: 'amountOutMin', type: 'uint256' },
          { internalType: 'address[]', name: 'path', type: 'address[]' },
          { internalType: 'address', name: 'to', type: 'address' },
          { internalType: 'uint256', name: 'deadline', type: 'uint256' },
        ],
        name: 'swapExactTokensForTokens',
        outputs: [
          { internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' },
        ],
        stateMutability: 'nonpayable',
        type: 'function',
      },
    ];

    // Uniswap V2 Pair ABI (simplified)
    this.pairABI = [
      {
        inputs: [],
        name: 'getReserves',
        outputs: [
          { internalType: 'uint112', name: '_reserve0', type: 'uint112' },
          { internalType: 'uint112', name: '_reserve1', type: 'uint112' },
          {
            internalType: 'uint32',
            name: '_blockTimestampLast',
            type: 'uint32',
          },
        ],
        stateMutability: 'view',
        type: 'function',
      },
      {
        inputs: [],
        name: 'token0',
        outputs: [{ internalType: 'address', name: '', type: 'address' }],
        stateMutability: 'view',
        type: 'function',
      },
      {
        inputs: [],
        name: 'token1',
        outputs: [{ internalType: 'address', name: '', type: 'address' }],
        stateMutability: 'view',
        type: 'function',
      },
    ];
  }

  // Initialize DEX with router address
  initializeDEX(routerAddress) {
    this.contractManager.loadABI('UniswapRouter', this.routerABI);
    this.contractManager.createContract('UniswapRouter', routerAddress);
  }

  // Load trading pair
  async loadPair(pairAddress, token0Address, token1Address) {
    this.contractManager.loadABI('UniswapPair', this.pairABI);
    const pairContract = this.contractManager.createContract(
      `Pair_${pairAddress}`,
      pairAddress
    );

    // Verify token addresses
    const contractToken0 = await this.contractManager.callMethod(
      `Pair_${pairAddress}`,
      'token0'
    );
    const contractToken1 = await this.contractManager.callMethod(
      `Pair_${pairAddress}`,
      'token1'
    );

    const pairInfo = {
      address: pairAddress,
      token0: contractToken0.toLowerCase(),
      token1: contractToken1.toLowerCase(),
      contract: pairContract,
    };

    this.pairs.set(
      `${token0Address.toLowerCase()}_${token1Address.toLowerCase()}`,
      pairInfo
    );
    this.pairs.set(
      `${token1Address.toLowerCase()}_${token0Address.toLowerCase()}`,
      pairInfo
    );

    return pairInfo;
  }

  // Get pair reserves
  async getPairReserves(token0Address, token1Address) {
    const pairKey = `${token0Address.toLowerCase()}_${token1Address.toLowerCase()}`;
    const pairInfo = this.pairs.get(pairKey);

    if (!pairInfo) {
      throw new Error('Pair not loaded');
    }

    const reserves = await this.contractManager.callMethod(
      `Pair_${pairInfo.address}`,
      'getReserves'
    );

    // Determine correct order
    const isToken0First = token0Address.toLowerCase() === pairInfo.token0;

    return {
      reserve0: isToken0First ? reserves._reserve0 : reserves._reserve1,
      reserve1: isToken0First ? reserves._reserve1 : reserves._reserve0,
      blockTimestampLast: reserves._blockTimestampLast,
    };
  }

  // Calculate swap amounts
  async getAmountsOut(amountIn, path) {
    const amounts = await this.contractManager.callMethod(
      'UniswapRouter',
      'getAmountsOut',
      amountIn,
      path
    );

    return amounts;
  }

  // Calculate price impact
  async calculatePriceImpact(amountIn, token0Address, token1Address) {
    const reserves = await this.getPairReserves(token0Address, token1Address);

    const reserve0 = parseFloat(reserves.reserve0);
    const reserve1 = parseFloat(reserves.reserve1);

    // Current price
    const currentPrice = reserve1 / reserve0;

    // Price after swap (simplified calculation)
    const newReserve0 = reserve0 + parseFloat(amountIn);
    const newReserve1 = (reserve0 * reserve1) / newReserve0; // Constant product
    const newPrice = newReserve1 / newReserve0;

    const priceImpact =
      Math.abs((newPrice - currentPrice) / currentPrice) * 100;

    return {
      currentPrice,
      newPrice,
      priceImpact: priceImpact.toFixed(4),
    };
  }

  // Swap ETH for tokens
  async swapETHForTokens(amountETH, tokenAddress, slippageTolerance = 0.5) {
    const path = [this.getWETHAddress(), tokenAddress];
    const deadline = Math.floor(Date.now() / 1000) + 300; // 5 minutes

    // Get expected output
    const amountInWei = this.web3.utils.toWei(amountETH.toString(), 'ether');
    const amountsOut = await this.getAmountsOut(amountInWei, path);

    // Calculate minimum output with slippage
    const expectedOutput = amountsOut[1];
    const minOutput = expectedOutput * (1 - slippageTolerance / 100);

    return await this.contractManager.sendMethod(
      'UniswapRouter',
      'swapExactETHForTokens',
      { value: amountInWei },
      minOutput.toString(),
      path,
      this.contractManager.web3Manager.account,
      deadline
    );
  }

  // Swap tokens for tokens
  async swapTokensForTokens(
    amountIn,
    tokenInAddress,
    tokenOutAddress,
    slippageTolerance = 0.5
  ) {
    const path = [tokenInAddress, tokenOutAddress];
    const deadline = Math.floor(Date.now() / 1000) + 300; // 5 minutes

    // Get expected output
    const amountsOut = await this.getAmountsOut(amountIn, path);
    const expectedOutput = amountsOut[1];
    const minOutput = expectedOutput * (1 - slippageTolerance / 100);

    // Approve token spending first
    await this.approveTokenSpending(tokenInAddress, amountIn);

    return await this.contractManager.sendMethod(
      'UniswapRouter',
      'swapExactTokensForTokens',
      {},
      amountIn,
      minOutput.toString(),
      path,
      this.contractManager.web3Manager.account,
      deadline
    );
  }

  // Approve token spending for router
  async approveTokenSpending(tokenAddress, amount) {
    const routerContract = this.contractManager.getContract('UniswapRouter');
    const routerAddress = routerContract.options.address;

    // Load token if not already loaded
    const tokenName = `Token_${tokenAddress}`;
    if (!this.contractManager.getContract(tokenName)) {
      this.tokenManager.loadToken(tokenName, tokenAddress);
    }

    return await this.tokenManager.approve(tokenName, routerAddress, amount);
  }

  // Get WETH address (varies by network)
  getWETHAddress() {
    const networkWETH = {
      1: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // Mainnet
      5: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', // Goerli
      137: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', // Polygon
    };

    return (
      networkWETH[this.contractManager.web3Manager.networkId] || networkWETH[1]
    );
  }

  // Monitor swap events
  monitorSwaps(callback) {
    // This would require listening to DEX events
    // Implementation depends on specific DEX contract events
  }
}

// Liquidity Pool Manager
class LiquidityPoolManager {
  constructor(dexManager) {
    this.dexManager = dexManager;
    this.contractManager = dexManager.contractManager;
    this.web3 = dexManager.web3;
    this.positions = new Map();
  }

  // Add liquidity to pool
  async addLiquidity(
    tokenAAddress,
    tokenBAddress,
    amountA,
    amountB,
    slippageTolerance = 0.5
  ) {
    const deadline = Math.floor(Date.now() / 1000) + 300; // 5 minutes

    // Calculate minimum amounts with slippage
    const minAmountA = amountA * (1 - slippageTolerance / 100);
    const minAmountB = amountB * (1 - slippageTolerance / 100);

    // Approve token spending
    await this.dexManager.approveTokenSpending(tokenAAddress, amountA);
    await this.dexManager.approveTokenSpending(tokenBAddress, amountB);

    // Add liquidity (this would require the addLiquidity method in router ABI)
    return await this.contractManager.sendMethod(
      'UniswapRouter',
      'addLiquidity',
      {},
      tokenAAddress,
      tokenBAddress,
      amountA.toString(),
      amountB.toString(),
      minAmountA.toString(),
      minAmountB.toString(),
      this.contractManager.web3Manager.account,
      deadline
    );
  }

  // Remove liquidity from pool
  async removeLiquidity(
    tokenAAddress,
    tokenBAddress,
    liquidity,
    slippageTolerance = 0.5
  ) {
    const deadline = Math.floor(Date.now() / 1000) + 300; // 5 minutes

    // Calculate minimum amounts
    const pairInfo = await this.dexManager.getPairReserves(
      tokenAAddress,
      tokenBAddress
    );
    const totalSupply = await this.getTotalSupply(pairInfo.address);

    const amountA = (liquidity * pairInfo.reserve0) / totalSupply;
    const amountB = (liquidity * pairInfo.reserve1) / totalSupply;

    const minAmountA = amountA * (1 - slippageTolerance / 100);
    const minAmountB = amountB * (1 - slippageTolerance / 100);

    return await this.contractManager.sendMethod(
      'UniswapRouter',
      'removeLiquidity',
      {},
      tokenAAddress,
      tokenBAddress,
      liquidity.toString(),
      minAmountA.toString(),
      minAmountB.toString(),
      this.contractManager.web3Manager.account,
      deadline
    );
  }

  // Get LP token balance
  async getLPTokenBalance(pairAddress, userAddress) {
    const pairContract = this.contractManager.getContract(
      `Pair_${pairAddress}`
    );
    if (!pairContract) {
      throw new Error('Pair contract not loaded');
    }

    return await this.contractManager.callMethod(
      `Pair_${pairAddress}`,
      'balanceOf',
      userAddress
    );
  }

  // Calculate LP token value
  async calculateLPValue(pairAddress, lpAmount) {
    const reserves = await this.dexManager.getPairReserves(
      this.pairs.get(pairAddress).token0,
      this.pairs.get(pairAddress).token1
    );

    const totalSupply = await this.getTotalSupply(pairAddress);

    const share = lpAmount / totalSupply;
    const value0 = reserves.reserve0 * share;
    const value1 = reserves.reserve1 * share;

    return { value0, value1, share };
  }

  // Get total supply of LP tokens
  async getTotalSupply(pairAddress) {
    return await this.contractManager.callMethod(
      `Pair_${pairAddress}`,
      'totalSupply'
    );
  }

  // Calculate impermanent loss
  calculateImpermanentLoss(initialPrice, currentPrice) {
    const priceRatio = currentPrice / initialPrice;
    const impermanentLoss = (2 * Math.sqrt(priceRatio)) / (1 + priceRatio) - 1;

    return {
      percentage: (impermanentLoss * 100).toFixed(4),
      multiplier: (1 + impermanentLoss).toFixed(4),
    };
  }
}

// Yield Farming Manager
class YieldFarmingManager {
  constructor(contractManager) {
    this.contractManager = contractManager;
    this.web3 = contractManager.web3;
    this.farms = new Map();

    // Masterchef-style farming contract ABI (simplified)
    this.farmABI = [
      {
        inputs: [
          { internalType: 'uint256', name: '_pid', type: 'uint256' },
          { internalType: 'uint256', name: '_amount', type: 'uint256' },
        ],
        name: 'deposit',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
      },
      {
        inputs: [
          { internalType: 'uint256', name: '_pid', type: 'uint256' },
          { internalType: 'uint256', name: '_amount', type: 'uint256' },
        ],
        name: 'withdraw',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
      },
      {
        inputs: [
          { internalType: 'uint256', name: '_pid', type: 'uint256' },
          { internalType: 'address', name: '_user', type: 'address' },
        ],
        name: 'pendingReward',
        outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
        stateMutability: 'view',
        type: 'function',
      },
      {
        inputs: [
          { internalType: 'uint256', name: '_pid', type: 'uint256' },
          { internalType: 'address', name: '_user', type: 'address' },
        ],
        name: 'userInfo',
        outputs: [
          { internalType: 'uint256', name: 'amount', type: 'uint256' },
          { internalType: 'uint256', name: 'rewardDebt', type: 'uint256' },
        ],
        stateMutability: 'view',
        type: 'function',
      },
    ];
  }

  // Initialize farm contract
  initializeFarm(farmName, farmAddress) {
    this.contractManager.loadABI(farmName, this.farmABI);
    this.contractManager.createContract(farmName, farmAddress);

    this.farms.set(farmName, {
      address: farmAddress,
      pools: new Map(),
    });
  }

  // Add pool information
  addPool(farmName, poolId, stakingToken, rewardToken, apr = 0) {
    const farm = this.farms.get(farmName);
    if (!farm) {
      throw new Error('Farm not initialized');
    }

    farm.pools.set(poolId, {
      stakingToken,
      rewardToken,
      apr,
    });
  }

  // Stake tokens in farm
  async stake(farmName, poolId, amount) {
    const farm = this.farms.get(farmName);
    const pool = farm.pools.get(poolId);

    if (!pool) {
      throw new Error('Pool not found');
    }

    // Approve staking token
    const stakingTokenName = `Token_${pool.stakingToken}`;
    if (!this.contractManager.getContract(stakingTokenName)) {
      this.contractManager.tokenManager.loadToken(
        stakingTokenName,
        pool.stakingToken
      );
    }

    await this.contractManager.tokenManager.approve(
      stakingTokenName,
      farm.address,
      amount
    );

    // Deposit to farm
    return await this.contractManager.sendMethod(
      farmName,
      'deposit',
      {},
      poolId,
      amount.toString()
    );
  }

  // Unstake tokens from farm
  async unstake(farmName, poolId, amount) {
    return await this.contractManager.sendMethod(
      farmName,
      'withdraw',
      {},
      poolId,
      amount.toString()
    );
  }

  // Get pending rewards
  async getPendingRewards(farmName, poolId, userAddress) {
    return await this.contractManager.callMethod(
      farmName,
      'pendingReward',
      poolId,
      userAddress
    );
  }

  // Get user staking info
  async getUserInfo(farmName, poolId, userAddress) {
    const userInfo = await this.contractManager.callMethod(
      farmName,
      'userInfo',
      poolId,
      userAddress
    );

    return {
      stakedAmount: userInfo.amount.toString(),
      rewardDebt: userInfo.rewardDebt.toString(),
    };
  }

  // Calculate estimated rewards
  calculateEstimatedRewards(stakedAmount, apr, days) {
    const dailyRate = apr / 365 / 100;
    const estimatedRewards = stakedAmount * dailyRate * days;

    return estimatedRewards;
  }

  // Monitor farming positions
  async monitorFarms(userAddress) {
    const positions = [];

    for (const [farmName, farm] of this.farms) {
      for (const [poolId, pool] of farm.pools) {
        try {
          const userInfo = await this.getUserInfo(
            farmName,
            poolId,
            userAddress
          );
          const pendingRewards = await this.getPendingRewards(
            farmName,
            poolId,
            userAddress
          );

          if (parseFloat(userInfo.stakedAmount) > 0) {
            positions.push({
              farmName,
              poolId,
              stakingToken: pool.stakingToken,
              rewardToken: pool.rewardToken,
              stakedAmount: userInfo.stakedAmount,
              pendingRewards: pendingRewards.toString(),
              apr: pool.apr,
            });
          }
        } catch (error) {
          console.error(
            `Error monitoring farm ${farmName} pool ${poolId}:`,
            error
          );
        }
      }
    }

    return positions;
  }
}

// Usage example
async function initializeDeFiApp() {
  try {
    // Initialize Web3
    const web3Manager = new Web3Manager();
    await web3Manager.initialize();

    // Initialize managers
    const contractManager = new SmartContractManager(web3Manager);
    const tokenManager = new ERC20TokenManager(contractManager);
    const dexManager = new DEXManager(contractManager, tokenManager);
    const liquidityManager = new LiquidityPoolManager(dexManager);
    const farmingManager = new YieldFarmingManager(contractManager);

    // Initialize DEX (Uniswap V2 Router address - varies by network)
    const routerAddress = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'; // Mainnet
    dexManager.initializeDEX(routerAddress);

    console.log('DeFi app initialized');

    // Example: Get token price
    try {
      const wethAddress = dexManager.getWETHAddress();
      const usdcAddress = '0xA0b86a33E6441B8A4B8A45C0C26e9DFF1c66ec59'; // Example

      const amountIn = web3Manager.toWei('1', 'ether'); // 1 ETH
      const path = [wethAddress, usdcAddress];

      const amountsOut = await dexManager.getAmountsOut(amountIn, path);
      console.log('1 ETH =', web3Manager.fromWei(amountsOut[1]), 'USDC');
    } catch (error) {
      console.log(
        'Price query failed (expected if not on mainnet):',
        error.message
      );
    }

    return {
      web3Manager,
      contractManager,
      tokenManager,
      dexManager,
      liquidityManager,
      farmingManager,
    };
  } catch (error) {
    console.error('DeFi app initialization failed:', error);
    throw error;
  }
}

// Initialize the DeFi app
initializeDeFiApp()
  .then((result) => {
    console.log('DeFi app initialized successfully');
  })
  .catch((error) => {
    console.error('Failed to initialize DeFi app:', error);
  });

NFT Marketplace Development

NFT Contract Integration

// NFT Manager for ERC-721 tokens
class NFTManager {
  constructor(contractManager) {
    this.contractManager = contractManager;
    this.web3 = contractManager.web3;
    this.collections = new Map();

    // ERC-721 ABI (simplified)
    this.erc721ABI = [
      {
        inputs: [{ name: 'owner', type: 'address' }],
        name: 'balanceOf',
        outputs: [{ name: '', type: 'uint256' }],
        stateMutability: 'view',
        type: 'function',
      },
      {
        inputs: [{ name: 'tokenId', type: 'uint256' }],
        name: 'ownerOf',
        outputs: [{ name: '', type: 'address' }],
        stateMutability: 'view',
        type: 'function',
      },
      {
        inputs: [{ name: 'tokenId', type: 'uint256' }],
        name: 'tokenURI',
        outputs: [{ name: '', type: 'string' }],
        stateMutability: 'view',
        type: 'function',
      },
      {
        inputs: [
          { name: 'from', type: 'address' },
          { name: 'to', type: 'address' },
          { name: 'tokenId', type: 'uint256' },
        ],
        name: 'transferFrom',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
      },
      {
        inputs: [
          { name: 'to', type: 'address' },
          { name: 'approved', type: 'address' },
        ],
        name: 'setApprovalForAll',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
      },
      {
        inputs: [
          { name: 'owner', type: 'address' },
          { name: 'operator', type: 'address' },
        ],
        name: 'isApprovedForAll',
        outputs: [{ name: '', type: 'bool' }],
        stateMutability: 'view',
        type: 'function',
      },
      {
        anonymous: false,
        inputs: [
          { indexed: true, name: 'from', type: 'address' },
          { indexed: true, name: 'to', type: 'address' },
          { indexed: true, name: 'tokenId', type: 'uint256' },
        ],
        name: 'Transfer',
        type: 'event',
      },
    ];
  }

  // Load NFT collection
  loadCollection(collectionName, contractAddress) {
    this.contractManager.loadABI(collectionName, this.erc721ABI);
    const contract = this.contractManager.createContract(
      collectionName,
      contractAddress
    );

    this.collections.set(collectionName, {
      address: contractAddress,
      contract,
    });

    return contract;
  }

  // Get NFT balance
  async getBalance(collectionName, ownerAddress) {
    return await this.contractManager.callMethod(
      collectionName,
      'balanceOf',
      ownerAddress
    );
  }

  // Get NFT owner
  async getOwner(collectionName, tokenId) {
    return await this.contractManager.callMethod(
      collectionName,
      'ownerOf',
      tokenId
    );
  }

  // Get NFT metadata URI
  async getTokenURI(collectionName, tokenId) {
    return await this.contractManager.callMethod(
      collectionName,
      'tokenURI',
      tokenId
    );
  }

  // Get NFT metadata from URI
  async getTokenMetadata(uri) {
    try {
      // Handle IPFS URIs
      if (uri.startsWith('ipfs://')) {
        uri = uri.replace('ipfs://', 'https://ipfs.io/ipfs/');
      }

      const response = await fetch(uri);
      return await response.json();
    } catch (error) {
      console.error('Failed to fetch metadata:', error);
      return null;
    }
  }

  // Transfer NFT
  async transferNFT(collectionName, fromAddress, toAddress, tokenId) {
    return await this.contractManager.sendMethod(
      collectionName,
      'transferFrom',
      {},
      fromAddress,
      toAddress,
      tokenId
    );
  }

  // Approve marketplace for all NFTs
  async setApprovalForAll(collectionName, operatorAddress, approved) {
    return await this.contractManager.sendMethod(
      collectionName,
      'setApprovalForAll',
      {},
      operatorAddress,
      approved
    );
  }

  // Check approval status
  async isApprovedForAll(collectionName, ownerAddress, operatorAddress) {
    return await this.contractManager.callMethod(
      collectionName,
      'isApprovedForAll',
      ownerAddress,
      operatorAddress
    );
  }

  // Get owned NFTs
  async getOwnedNFTs(collectionName, ownerAddress) {
    const balance = await this.getBalance(collectionName, ownerAddress);
    const ownedNFTs = [];

    // This is a simplified approach - in practice, you'd need to track token IDs
    // or use events to find owned tokens efficiently
    const totalSupply = 10000; // Example max supply

    for (let tokenId = 1; tokenId <= Math.min(totalSupply, 1000); tokenId++) {
      try {
        const owner = await this.getOwner(collectionName, tokenId);
        if (owner.toLowerCase() === ownerAddress.toLowerCase()) {
          const uri = await this.getTokenURI(collectionName, tokenId);
          const metadata = await this.getTokenMetadata(uri);

          ownedNFTs.push({
            tokenId,
            uri,
            metadata,
          });
        }
      } catch (error) {
        // Token doesn't exist or other error
        continue;
      }
    }

    return ownedNFTs;
  }

  // Monitor NFT transfers
  monitorTransfers(collectionName, options = {}) {
    return this.contractManager.subscribeToEvent(
      collectionName,
      'Transfer',
      options
    );
  }
}

// NFT Marketplace Manager
class NFTMarketplaceManager {
  constructor(contractManager, nftManager) {
    this.contractManager = contractManager;
    this.nftManager = nftManager;
    this.web3 = contractManager.web3;
    this.marketplace = null;

    // Marketplace ABI (simplified)
    this.marketplaceABI = [
      {
        inputs: [
          { name: 'nftContract', type: 'address' },
          { name: 'tokenId', type: 'uint256' },
          { name: 'price', type: 'uint256' },
        ],
        name: 'listItem',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
      },
      {
        inputs: [
          { name: 'nftContract', type: 'address' },
          { name: 'tokenId', type: 'uint256' },
        ],
        name: 'buyItem',
        outputs: [],
        stateMutability: 'payable',
        type: 'function',
      },
      {
        inputs: [
          { name: 'nftContract', type: 'address' },
          { name: 'tokenId', type: 'uint256' },
        ],
        name: 'cancelListing',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
      },
      {
        inputs: [
          { name: 'nftContract', type: 'address' },
          { name: 'tokenId', type: 'uint256' },
        ],
        name: 'getListing',
        outputs: [
          { name: 'seller', type: 'address' },
          { name: 'price', type: 'uint256' },
          { name: 'isActive', type: 'bool' },
        ],
        stateMutability: 'view',
        type: 'function',
      },
      {
        anonymous: false,
        inputs: [
          { indexed: true, name: 'seller', type: 'address' },
          { indexed: true, name: 'nftContract', type: 'address' },
          { indexed: true, name: 'tokenId', type: 'uint256' },
          { indexed: false, name: 'price', type: 'uint256' },
        ],
        name: 'ItemListed',
        type: 'event',
      },
      {
        anonymous: false,
        inputs: [
          { indexed: true, name: 'buyer', type: 'address' },
          { indexed: true, name: 'nftContract', type: 'address' },
          { indexed: true, name: 'tokenId', type: 'uint256' },
          { indexed: false, name: 'price', type: 'uint256' },
        ],
        name: 'ItemSold',
        type: 'event',
      },
    ];
  }

  // Initialize marketplace
  initializeMarketplace(marketplaceAddress) {
    this.contractManager.loadABI('Marketplace', this.marketplaceABI);
    this.marketplace = this.contractManager.createContract(
      'Marketplace',
      marketplaceAddress
    );
    return this.marketplace;
  }

  // List NFT for sale
  async listNFT(nftContract, tokenId, priceInETH) {
    // First, approve marketplace to transfer the NFT
    const collectionName = this.getCollectionName(nftContract);
    await this.nftManager.setApprovalForAll(
      collectionName,
      this.marketplace.options.address,
      true
    );

    const priceInWei = this.web3.utils.toWei(priceInETH.toString(), 'ether');

    return await this.contractManager.sendMethod(
      'Marketplace',
      'listItem',
      {},
      nftContract,
      tokenId,
      priceInWei
    );
  }

  // Buy NFT from marketplace
  async buyNFT(nftContract, tokenId) {
    const listing = await this.getListing(nftContract, tokenId);

    if (!listing.isActive) {
      throw new Error('Item not listed for sale');
    }

    return await this.contractManager.sendMethod(
      'Marketplace',
      'buyItem',
      { value: listing.price },
      nftContract,
      tokenId
    );
  }

  // Cancel NFT listing
  async cancelListing(nftContract, tokenId) {
    return await this.contractManager.sendMethod(
      'Marketplace',
      'cancelListing',
      {},
      nftContract,
      tokenId
    );
  }

  // Get listing information
  async getListing(nftContract, tokenId) {
    const listing = await this.contractManager.callMethod(
      'Marketplace',
      'getListing',
      nftContract,
      tokenId
    );

    return {
      seller: listing.seller,
      price: listing.price,
      priceInETH: this.web3.utils.fromWei(listing.price, 'ether'),
      isActive: listing.isActive,
    };
  }

  // Get all listings for a collection
  async getCollectionListings(nftContract, maxTokens = 1000) {
    const listings = [];

    for (let tokenId = 1; tokenId <= maxTokens; tokenId++) {
      try {
        const listing = await this.getListing(nftContract, tokenId);

        if (listing.isActive) {
          const collectionName = this.getCollectionName(nftContract);
          const uri = await this.nftManager.getTokenURI(
            collectionName,
            tokenId
          );
          const metadata = await this.nftManager.getTokenMetadata(uri);

          listings.push({
            nftContract,
            tokenId,
            ...listing,
            uri,
            metadata,
          });
        }
      } catch (error) {
        // Token doesn't exist or not listed
        continue;
      }
    }

    return listings;
  }

  // Get user's listings
  async getUserListings(userAddress) {
    // This would typically be done by querying events
    // For now, we'll check known collections
    const userListings = [];

    for (const [collectionName, collection] of this.nftManager.collections) {
      try {
        const listings = await this.getCollectionListings(collection.address);
        const userCollectionListings = listings.filter(
          (listing) =>
            listing.seller.toLowerCase() === userAddress.toLowerCase()
        );
        userListings.push(...userCollectionListings);
      } catch (error) {
        console.error(`Error getting listings for ${collectionName}:`, error);
      }
    }

    return userListings;
  }

  // Monitor marketplace events
  monitorItemListed(callback) {
    return this.contractManager
      .subscribeToEvent('Marketplace', 'ItemListed', {})
      .on('data', callback);
  }

  monitorItemSold(callback) {
    return this.contractManager
      .subscribeToEvent('Marketplace', 'ItemSold', {})
      .on('data', callback);
  }

  // Calculate marketplace fees
  calculateFees(price, feePercentage = 2.5) {
    const fee = price * (feePercentage / 100);
    const sellerReceives = price - fee;

    return {
      totalPrice: price,
      marketplaceFee: fee,
      sellerReceives,
      feePercentage,
    };
  }

  // Helper method to get collection name from address
  getCollectionName(address) {
    for (const [name, collection] of this.nftManager.collections) {
      if (collection.address.toLowerCase() === address.toLowerCase()) {
        return name;
      }
    }
    return `Collection_${address}`;
  }
}

// NFT Analytics Manager
class NFTAnalyticsManager {
  constructor(nftManager, marketplaceManager) {
    this.nftManager = nftManager;
    this.marketplaceManager = marketplaceManager;
    this.web3 = nftManager.web3;
    this.priceHistory = new Map();
    this.floorPrices = new Map();
  }

  // Calculate collection floor price
  async calculateFloorPrice(nftContract) {
    const listings =
      await this.marketplaceManager.getCollectionListings(nftContract);

    if (listings.length === 0) {
      return null;
    }

    const prices = listings.map((listing) => parseFloat(listing.priceInETH));
    const floorPrice = Math.min(...prices);

    this.floorPrices.set(nftContract, {
      price: floorPrice,
      timestamp: Date.now(),
      listingCount: listings.length,
    });

    return floorPrice;
  }

  // Calculate collection volume
  async calculateVolume(nftContract, timeRange = 86400000) {
    // 24 hours
    // This would typically query past sale events
    // For demonstration, we'll use a simplified approach
    const currentTime = Date.now();
    const fromTime = currentTime - timeRange;

    try {
      const soldEvents =
        await this.marketplaceManager.contractManager.getPastEvents(
          'Marketplace',
          'ItemSold',
          {
            filter: { nftContract },
            fromBlock: 'earliest',
            toBlock: 'latest',
          }
        );

      let volume = 0;
      let salesCount = 0;

      soldEvents.forEach((event) => {
        const eventTime = event.blockNumber * 15000; // Rough timestamp estimate
        if (eventTime >= fromTime) {
          volume += parseFloat(
            this.web3.utils.fromWei(event.returnValues.price, 'ether')
          );
          salesCount++;
        }
      });

      return {
        volume,
        salesCount,
        averagePrice: salesCount > 0 ? volume / salesCount : 0,
      };
    } catch (error) {
      console.error('Error calculating volume:', error);
      return { volume: 0, salesCount: 0, averagePrice: 0 };
    }
  }

  // Get collection statistics
  async getCollectionStats(nftContract) {
    const floorPrice = await this.calculateFloorPrice(nftContract);
    const volume24h = await this.calculateVolume(nftContract);
    const listings =
      await this.marketplaceManager.getCollectionListings(nftContract);

    return {
      floorPrice,
      volume24h: volume24h.volume,
      sales24h: volume24h.salesCount,
      averagePrice24h: volume24h.averagePrice,
      totalListings: listings.length,
      uniqueOwners: await this.calculateUniqueOwners(nftContract),
      totalSupply: await this.getTotalSupply(nftContract),
    };
  }

  // Calculate unique owners (simplified)
  async calculateUniqueOwners(nftContract) {
    // This would typically be done by analyzing transfer events
    // For demonstration, return a placeholder
    return Math.floor(Math.random() * 1000) + 100;
  }

  // Get total supply (if supported)
  async getTotalSupply(nftContract) {
    try {
      const collectionName =
        this.marketplaceManager.getCollectionName(nftContract);
      return await this.nftManager.contractManager.callMethod(
        collectionName,
        'totalSupply'
      );
    } catch (error) {
      // Method not supported
      return null;
    }
  }

  // Track price changes
  trackPriceChange(nftContract, tokenId, oldPrice, newPrice) {
    const key = `${nftContract}_${tokenId}`;

    if (!this.priceHistory.has(key)) {
      this.priceHistory.set(key, []);
    }

    this.priceHistory.get(key).push({
      timestamp: Date.now(),
      oldPrice,
      newPrice,
      change: newPrice - oldPrice,
      changePercent: ((newPrice - oldPrice) / oldPrice) * 100,
    });
  }

  // Get trending collections
  async getTrendingCollections() {
    const collections = Array.from(this.nftManager.collections.keys());
    const trending = [];

    for (const collection of collections) {
      const stats = await this.getCollectionStats(
        this.nftManager.collections.get(collection).address
      );

      trending.push({
        collection,
        ...stats,
      });
    }

    // Sort by 24h volume
    return trending.sort((a, b) => b.volume24h - a.volume24h);
  }
}

// Usage example
async function initializeNFTApp() {
  try {
    // Initialize Web3
    const web3Manager = new Web3Manager();
    await web3Manager.initialize();

    // Initialize managers
    const contractManager = new SmartContractManager(web3Manager);
    const nftManager = new NFTManager(contractManager);
    const marketplaceManager = new NFTMarketplaceManager(
      contractManager,
      nftManager
    );
    const analyticsManager = new NFTAnalyticsManager(
      nftManager,
      marketplaceManager
    );

    console.log('NFT app initialized');

    // Example: Load a popular NFT collection
    const collectionAddress = '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D'; // BAYC example
    nftManager.loadCollection('BoredApes', collectionAddress);

    // Example: Initialize marketplace
    const marketplaceAddress = '0x495f947276749Ce646f68AC8c248420045cb7b5e'; // Example address
    marketplaceManager.initializeMarketplace(marketplaceAddress);

    console.log('NFT contracts loaded');

    return {
      web3Manager,
      contractManager,
      nftManager,
      marketplaceManager,
      analyticsManager,
    };
  } catch (error) {
    console.error('NFT app initialization failed:', error);
    throw error;
  }
}

// Initialize the NFT app
initializeNFTApp()
  .then((result) => {
    console.log('NFT app initialized successfully');
  })
  .catch((error) => {
    console.error('Failed to initialize NFT app:', error);
  });

Conclusion

JavaScript blockchain development opens up powerful possibilities for building decentralized applications that interact with blockchain networks. Through Web3.js and smart contract integration, developers can create applications for DeFi trading, NFT marketplaces, yield farming, and more. The key to successful blockchain development is understanding smart contract interactions, handling asynchronous operations properly, and implementing robust error handling for network issues.

When building blockchain applications with JavaScript, focus on security best practices, efficient transaction handling, proper event monitoring, and user-friendly wallet integration. Consider implementing proper caching strategies for blockchain data, monitoring gas prices for optimal transaction timing, and providing clear feedback to users about transaction states and confirmations.