> ## Documentation Index
> Fetch the complete documentation index at: https://docs.layerzero.network/llms.txt
> Use this file to discover all available pages before exploring further.

# DVN Providers

> Learn about DVN Providers in LayerZero V2. Understand the architecture, core concepts, and how it enables omnichain interoperability. Security configuration ...

export const DvnAddressesTable = ({stage}) => {
  const safeString = str => (str || '').toString();
  const getExplorerUrl = deployment => {
    const chainKey = safeString(deployment?.chainKey);
    return `https://layerzeroscan.com/api/explorer/${chainKey}/address`;
  };
  const [isDark, setIsDark] = useState(true);
  const [deploymentsData, setDeploymentsData] = useState([]);
  const [isLoadingData, setIsLoadingData] = useState(true);
  const [loadError, setLoadError] = useState(null);
  const [selectedStage, setSelectedStage] = useState([]);
  const [selectedDvns, setSelectedDvns] = useState([]);
  const [selectedChains, setSelectedChains] = useState([]);
  const [showReadCompatibleOnly, setShowReadCompatibleOnly] = useState(false);
  const [dvnSearchTerm, setDvnSearchTerm] = useState('');
  const [networkSearchTerm, setNetworkSearchTerm] = useState('');
  const [isDvnSearchOpen, setIsDvnSearchOpen] = useState(false);
  const [isNetworkSearchOpen, setIsNetworkSearchOpen] = useState(false);
  const [copiedText, setCopiedText] = useState(null);
  const [failedIcons, setFailedIcons] = useState({});
  const [displayLimit, setDisplayLimit] = useState(50);
  const dvnSearchRef = useRef(null);
  const networkSearchRef = useRef(null);
  useEffect(() => {
    if (typeof document === 'undefined') return;
    const checkTheme = () => {
      setIsDark(document.documentElement.classList.contains('dark'));
    };
    checkTheme();
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  const tokens = {
    bgMain: isDark ? '#09090b' : '#ffffff',
    bgSecondary: isDark ? '#18181b' : '#f4f4f5',
    bgHover: isDark ? '#27272a' : '#e4e4e7',
    divider: isDark ? '#27272a' : '#e4e4e7',
    textPrimary: isDark ? '#fafafa' : '#09090b',
    textSecondary: isDark ? '#a1a1aa' : '#71717a',
    textTertiary: isDark ? '#71717a' : '#a1a1aa',
    accent: '#a77dff',
    accentHover: '#8b5cf6',
    space1: '4px',
    space2: '8px',
    space3: '12px',
    space4: '16px',
    space6: '24px',
    radiusSm: '4px',
    radiusMd: '8px',
    radiusLg: '12px',
    radiusFull: '9999px',
    shadowSm: isDark ? '0 1px 2px rgba(0,0,0,0.4)' : '0 1px 2px rgba(0,0,0,0.05)',
    shadowLg: isDark ? '0 10px 40px rgba(0,0,0,0.5)' : '0 10px 40px rgba(0,0,0,0.15)'
  };
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const params = new URLSearchParams(window.location.search);
    const initialDvns = params.get('dvns') ? params.get('dvns').split(',') : [];
    const initialChains = params.get('chains') ? params.get('chains').split(',') : [];
    setSelectedDvns(initialDvns);
    setSelectedChains(initialChains);
  }, []);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const searchParams = new URLSearchParams();
    if (selectedDvns.length > 0) {
      searchParams.set('dvns', selectedDvns.join(','));
    }
    if (selectedChains.length > 0) {
      searchParams.set('chains', selectedChains.join(','));
    }
    const newUrl = searchParams.toString() ? `?${searchParams.toString()}` : window.location.pathname;
    window.history.replaceState({}, '', newUrl);
  }, [selectedDvns, selectedChains]);
  const fetchLocalFile = useCallback(function (path) {
    return fetch('/public' + path).then(function (r) {
      if (!r.ok) throw new Error();
      return r.text();
    }).then(function (t) {
      if (t.trim().startsWith('<')) throw new Error();
      return t;
    }).catch(function () {
      return fetch(path).then(function (r) {
        if (!r.ok) throw new Error();
        return r.text();
      }).then(function (t) {
        if (t.trim().startsWith('<')) throw new Error();
        return t;
      });
    });
  }, []);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    setIsLoadingData(true);
    fetchLocalFile('/data/dvnDeployments.json').then(function (text) {
      var data = JSON.parse(text);
      if (Array.isArray(data)) {
        setDeploymentsData(data);
        setIsLoadingData(false);
      } else {
        throw new Error('Unexpected data format');
      }
    }).catch(function () {
      fetch('https://metadata.layerzero-api.com/v1/metadata/dvns').then(function (r) {
        if (!r.ok) throw new Error('Failed to fetch from API');
        return r.json();
      }).then(function (data) {
        var transformedData = [];
        if (data && typeof data === 'object') {
          Object.entries(data).forEach(function (entry) {
            var chainName = entry[0];
            var chainData = entry[1];
            if (!chainData || !chainData.dvns) return;
            var chainDisplayName = chainData.chainName || chainName;
            var environment = chainData.environment || 'mainnet';
            Object.entries(chainData.dvns).forEach(function (dvnEntry) {
              var dvnAddress = dvnEntry[0];
              var dvnInfo = dvnEntry[1];
              if (!dvnInfo || dvnInfo.deprecated) return;
              transformedData.push({
                eid: 0,
                chainKey: chainName,
                nativeChainId: 0,
                stage: environment,
                version: dvnInfo.version || 2,
                canonicalName: dvnInfo.canonicalName || dvnInfo.id,
                id: dvnInfo.id || dvnInfo.canonicalName,
                dvnDisplayName: dvnInfo.canonicalName || dvnInfo.id,
                dvnAddress: dvnAddress,
                chainDisplayName: chainDisplayName,
                lzReadCompatible: dvnInfo.lzReadCompatible || false
              });
            });
          });
        }
        setDeploymentsData(transformedData);
      }).catch(function (apiErr) {
        console.error('Error loading from API:', apiErr);
        setLoadError(apiErr);
      }).finally(function () {
        setIsLoadingData(false);
      });
    });
  }, []);
  const handleCopy = useCallback((text, e) => {
    if (e) {
      e.preventDefault();
      e.stopPropagation();
    }
    if (typeof navigator !== 'undefined' && navigator.clipboard) {
      navigator.clipboard.writeText(text);
      setCopiedText(text);
      setTimeout(() => setCopiedText(null), 2000);
    }
  }, []);
  const CopyButton = ({text}) => {
    const isCopied = copiedText === text;
    const [isHovered, setIsHovered] = useState(false);
    return <button onClick={e => handleCopy(text, e)} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} style={{
      background: 'none',
      border: 'none',
      cursor: 'pointer',
      padding: '4px',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      opacity: isHovered ? 1 : 0.5,
      transition: 'opacity 0.2s',
      flexShrink: 0
    }}>
        {isCopied ? <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
            <path d="M2 7L5.5 10.5L12 4" stroke="#22c55e" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
          </svg> : <svg width="14" height="14" viewBox="0 0 20 20" fill="none">
            <path fillRule="evenodd" clipRule="evenodd" d="M4 3H14V5H15V3V2H14H4H3V3V15V16H4H6V15H4V3Z" fill={tokens.textSecondary} />
            <rect x="6.5" y="5.5" width="11" height="13" stroke={tokens.textSecondary} />
          </svg>}
      </button>;
  };
  const getInitials = name => {
    if (!name) return '?';
    const words = name.toString().split(' ');
    if (words.length === 1) {
      return name.slice(0, 2).toUpperCase();
    }
    return (words[0].charAt(0) + words[1].charAt(0)).toUpperCase();
  };
  const NetworkIcon = ({chainKey, displayName, size = 20}) => {
    const iconUrl = `https://icons-ckg.pages.dev/lz-scan/networks/${chainKey}.svg`;
    const hasFailed = failedIcons[`network-${chainKey}`];
    if (hasFailed) {
      return <div style={{
        width: size,
        height: size,
        minWidth: size,
        borderRadius: '50%',
        background: 'linear-gradient(135deg, #7c3aed 0%, #a855f7 100%)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: Math.max(8, size * 0.4),
        fontWeight: 600,
        color: '#fff',
        flexShrink: 0,
        margin: 0
      }}>
          {getInitials(displayName || chainKey)}
        </div>;
    }
    return <img src={iconUrl} alt="" style={{
      width: size,
      height: size,
      borderRadius: '50%',
      objectFit: 'contain',
      flexShrink: 0,
      margin: 0,
      display: 'block'
    }} onError={() => {
      setFailedIcons(prev => ({
        ...prev,
        [`network-${chainKey}`]: true
      }));
    }} />;
  };
  const DVNIcon = ({dvnKey, size = 20}) => {
    const iconUrl = `https://icons-ckg.pages.dev/lz-scan/dvns/${dvnKey}.svg`;
    const hasFailed = failedIcons[`dvn-${dvnKey}`];
    if (hasFailed) {
      return <div style={{
        width: size,
        height: size,
        minWidth: size,
        borderRadius: '50%',
        background: 'linear-gradient(135deg, #7c3aed 0%, #a855f7 100%)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: Math.max(8, size * 0.4),
        fontWeight: 600,
        color: '#fff',
        flexShrink: 0,
        margin: 0
      }}>
          {getInitials(dvnKey)}
        </div>;
    }
    return <img src={iconUrl} alt="" style={{
      width: size,
      height: size,
      borderRadius: '50%',
      objectFit: 'contain',
      flexShrink: 0,
      margin: 0,
      display: 'block'
    }} onError={() => {
      setFailedIcons(prev => ({
        ...prev,
        [`dvn-${dvnKey}`]: true
      }));
    }} />;
  };
  const selectedDvnsSet = useMemo(() => new Set(selectedDvns), [selectedDvns]);
  const selectedChainsSet = useMemo(() => new Set(selectedChains), [selectedChains]);
  const selectedStageSet = useMemo(() => new Set(selectedStage), [selectedStage]);
  const uniqueChains = useMemo(() => {
    const filteredDeployments = selectedStage.length > 0 ? deploymentsData.filter(d => selectedStage.includes(d.stage)) : deploymentsData;
    const unique = Array.from(new Set(filteredDeployments.map(d => d.chainDisplayName))).map(chainDisplayName => {
      const deployment = filteredDeployments.find(d => d.chainDisplayName === chainDisplayName);
      return {
        value: deployment.chainKey,
        label: chainDisplayName
      };
    });
    return unique.sort((a, b) => a.label.localeCompare(b.label));
  }, [deploymentsData, selectedStage]);
  const filteredData = useMemo(() => {
    const lowerDvnSearchTerm = dvnSearchTerm.toLowerCase();
    const lowerNetworkSearchTerm = networkSearchTerm.toLowerCase();
    let data = deploymentsData.filter(({stage: deploymentStage, dvnDisplayName, chainKey, chainDisplayName}) => {
      const matchesStage = selectedStage.length === 0 || selectedStageSet.has(deploymentStage);
      const matchesDvn = selectedDvns.length === 0 || selectedDvnsSet.has(dvnDisplayName);
      const matchesChain = selectedChains.length === 0 || selectedChainsSet.has(chainKey);
      const matchesDvnSearch = !dvnSearchTerm || dvnDisplayName.toLowerCase().includes(lowerDvnSearchTerm);
      const matchesNetworkSearch = !networkSearchTerm || chainDisplayName.toLowerCase().includes(lowerNetworkSearchTerm);
      return matchesStage && matchesDvn && matchesChain && matchesDvnSearch && matchesNetworkSearch;
    });
    if (showReadCompatibleOnly) {
      data = data.filter(d => d.lzReadCompatible);
    }
    const dvnGroups = new Map();
    data.forEach(deployment => {
      const dvnName = deployment.dvnDisplayName;
      if (!dvnGroups.has(dvnName)) {
        dvnGroups.set(dvnName, []);
      }
      dvnGroups.get(dvnName).push(deployment);
    });
    const totalSelectedNetworks = selectedChains.length;
    const groupedArray = Array.from(dvnGroups.entries()).map(([dvnDisplayName, deployments]) => {
      const chainMap = new Map();
      deployments.forEach(d => {
        const existing = chainMap.get(d.chainKey);
        if (!existing || (d.version || 0) > (existing.version || 0)) {
          chainMap.set(d.chainKey, d);
        }
      });
      const uniqueDeployments = Array.from(chainMap.values());
      const sortedDeployments = uniqueDeployments.sort((a, b) => a.chainDisplayName.localeCompare(b.chainDisplayName));
      const uniqueChainCount = new Set(sortedDeployments.map(d => d.chainKey)).size;
      return {
        dvnDisplayName,
        groupId: sortedDeployments[0]?.id || dvnDisplayName,
        deployments: sortedDeployments,
        networkCount: uniqueChainCount,
        coverage: totalSelectedNetworks > 0 ? `${uniqueChainCount}/${totalSelectedNetworks}` : '-'
      };
    });
    groupedArray.sort((a, b) => {
      if (b.networkCount !== a.networkCount) return b.networkCount - a.networkCount;
      return a.dvnDisplayName.localeCompare(b.dvnDisplayName);
    });
    return groupedArray;
  }, [deploymentsData, selectedStage, selectedStageSet, selectedDvns, selectedDvnsSet, selectedChains, selectedChainsSet, showReadCompatibleOnly, dvnSearchTerm, networkSearchTerm]);
  const totalDVNCount = useMemo(() => {
    const filteredDeployments = selectedStage.length > 0 ? deploymentsData.filter(({stage}) => selectedStage.includes(stage)) : deploymentsData.filter(({stage}) => stage === 'mainnet');
    return new Set(filteredDeployments.map(d => d.dvnDisplayName)).size;
  }, [deploymentsData, selectedStage]);
  const uniqueDvnResults = useMemo(() => {
    const uniqueDvnsMap = new Map();
    const uniqueNetworksMap = new Map();
    const lowerDvnSearch = dvnSearchTerm.toLowerCase();
    const lowerNetworkSearch = networkSearchTerm.toLowerCase();
    deploymentsData.forEach(deployment => {
      const matchesStage = selectedStage.length === 0 || selectedStageSet.has(deployment.stage);
      const dvnMatch = !dvnSearchTerm || deployment.dvnDisplayName.toLowerCase().includes(lowerDvnSearch);
      const networkMatch = !networkSearchTerm || deployment.chainDisplayName.toLowerCase().includes(lowerNetworkSearch);
      const isSelectedDvn = selectedDvnsSet.has(deployment.dvnDisplayName);
      const isSelectedChain = selectedChainsSet.has(deployment.chainKey);
      if ((dvnMatch || isSelectedDvn) && (matchesStage || isSelectedDvn)) {
        if (!uniqueDvnsMap.has(deployment.dvnDisplayName)) {
          uniqueDvnsMap.set(deployment.dvnDisplayName, deployment);
        }
      }
      if ((networkMatch || isSelectedChain) && (matchesStage || isSelectedChain)) {
        if (!uniqueNetworksMap.has(deployment.chainKey)) {
          uniqueNetworksMap.set(deployment.chainKey, {
            chainKey: deployment.chainKey,
            chainDisplayName: deployment.chainDisplayName
          });
        }
      }
    });
    return {
      dvns: Array.from(uniqueDvnsMap.values()).sort((a, b) => {
        const aSelected = selectedDvnsSet.has(a.dvnDisplayName);
        const bSelected = selectedDvnsSet.has(b.dvnDisplayName);
        if (aSelected && !bSelected) return -1;
        if (!aSelected && bSelected) return 1;
        return a.dvnDisplayName.localeCompare(b.dvnDisplayName);
      }),
      networks: Array.from(uniqueNetworksMap.values()).sort((a, b) => {
        const aSelected = selectedChainsSet.has(a.chainKey);
        const bSelected = selectedChainsSet.has(b.chainKey);
        if (aSelected && !bSelected) return -1;
        if (!aSelected && bSelected) return 1;
        return a.chainDisplayName.localeCompare(b.chainDisplayName);
      })
    };
  }, [deploymentsData, dvnSearchTerm, networkSearchTerm, selectedDvns, selectedDvnsSet, selectedChains, selectedChainsSet, selectedStage, selectedStageSet]);
  useEffect(() => {
    const handleClickOutside = event => {
      if (dvnSearchRef.current && !dvnSearchRef.current.contains(event.target)) {
        setIsDvnSearchOpen(false);
      }
      if (networkSearchRef.current && !networkSearchRef.current.contains(event.target)) {
        setIsNetworkSearchOpen(false);
      }
    };
    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, []);
  const handleReset = useCallback(() => {
    if (stage) {
      setSelectedStage([stage]);
    } else {
      setSelectedStage([]);
    }
    setSelectedDvns([]);
    setSelectedChains([]);
    setShowReadCompatibleOnly(false);
    setDvnSearchTerm('');
    setNetworkSearchTerm('');
    setDisplayLimit(50);
  }, [stage]);
  useEffect(() => {
    setDisplayLimit(50);
  }, [selectedStage, selectedDvns, selectedChains, dvnSearchTerm, networkSearchTerm, showReadCompatibleOnly]);
  const {limitedData, totalRowCount, displayedRowCount} = useMemo(() => {
    let rowCount = 0;
    let displayedCount = 0;
    const limited = [];
    for (const group of filteredData) {
      if (displayedCount >= displayLimit) break;
      const remainingSlots = displayLimit - displayedCount;
      if (group.deployments.length <= remainingSlots) {
        limited.push(group);
        displayedCount += group.deployments.length;
      } else {
        limited.push({
          ...group,
          deployments: group.deployments.slice(0, remainingSlots)
        });
        displayedCount += remainingSlots;
      }
      rowCount += group.deployments.length;
    }
    for (let i = limited.length; i < filteredData.length; i++) {
      rowCount += filteredData[i].deployments.length;
    }
    return {
      limitedData: limited,
      totalRowCount: filteredData.reduce((acc, g) => acc + g.deployments.length, 0),
      displayedRowCount: displayedCount
    };
  }, [filteredData, displayLimit]);
  const hasMoreData = displayedRowCount < totalRowCount;
  const hasSelectedChains = selectedChains.length > 0;
  if (isLoadingData) {
    return <div style={{
      padding: tokens.space6,
      textAlign: 'center',
      color: tokens.textSecondary,
      background: tokens.bgMain,
      borderRadius: tokens.radiusLg,
      border: `1px solid ${tokens.divider}`
    }}>
        Loading DVN data...
      </div>;
  }
  if (loadError) {
    return <div style={{
      padding: tokens.space6,
      textAlign: 'center',
      color: '#ef4444',
      background: tokens.bgMain,
      borderRadius: tokens.radiusLg,
      border: `1px solid ${tokens.divider}`
    }}>
        Error loading DVN data.
      </div>;
  }
  return <div className="dvn-root" style={{
    fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif',
    margin: 0,
    padding: 0,
    marginTop: '-16px'
  }}>
      <style>{`
        .dvn-table-container,
        .dvn-table-container * {
          margin-left: 0 !important;
          margin-right: 0 !important;
        }
        .dvn-table-container table {
          margin: 0 !important;
        }
        .dvn-table-container a,
        .dvn-view-metadata-link {
          text-decoration: none !important;
          border-bottom: none !important;
          box-shadow: none !important;
          background-image: none !important;
        }
        .dvn-view-metadata-link:hover {
          text-decoration: underline !important;
        }
        .dvn-table-container img {
          margin: 0 !important;
          margin-top: 0 !important;
          margin-bottom: 0 !important;
        }
        .dvn-table-container td {
          line-height: 1 !important;
        }
        .grow.table,
        div.table,
        .table {
          margin-top: 0 !important;
          padding-top: 0 !important;
          margin-bottom: 0 !important;
          padding-bottom: 0 !important;
        }
        [class*="--page-padding"] {
          margin-top: 0 !important;
          padding-top: 0 !important;
        }
        .dvn-table-wrapper {
          margin-top: 16px !important;
        }
      `}</style>
      {}
      <div style={{
    display: 'flex',
    alignItems: 'center',
    padding: tokens.space4,
    gap: tokens.space4,
    fontSize: '14px',
    background: tokens.bgMain,
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusLg,
    marginBottom: tokens.space4,
    flexWrap: 'wrap'
  }}>
        {}
        <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: tokens.space2,
    padding: `${tokens.space2} ${tokens.space3}`,
    background: tokens.bgSecondary,
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusMd,
    height: '40px',
    boxSizing: 'border-box',
    flexShrink: 0
  }}>
          <span style={{
    fontWeight: 500,
    color: tokens.textPrimary,
    whiteSpace: 'nowrap'
  }}>Connected DVNs</span>
          <span style={{
    background: tokens.accent,
    color: 'white',
    padding: `2px ${tokens.space2}`,
    borderRadius: tokens.radiusFull,
    fontSize: '12px',
    fontWeight: 600,
    minWidth: '24px',
    textAlign: 'center'
  }}>
            {totalDVNCount}
          </span>
        </div>

        {}
        {!stage && <div style={{
    display: 'flex',
    alignItems: 'center',
    padding: tokens.space1,
    background: tokens.bgSecondary,
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusMd,
    height: '40px',
    boxSizing: 'border-box'
  }}>
            {['All', 'Mainnet', 'Testnet'].map(label => {
    const value = label === 'All' ? [] : [label.toLowerCase()];
    const isActive = label === 'All' ? selectedStage.length === 0 : selectedStage.includes(label.toLowerCase());
    return <button key={label} onClick={() => setSelectedStage(value)} style={{
      padding: `${tokens.space2} ${tokens.space3}`,
      border: 'none',
      background: isActive ? tokens.accent : 'transparent',
      color: isActive ? 'white' : tokens.textSecondary,
      cursor: 'pointer',
      borderRadius: tokens.radiusSm,
      fontSize: '13px',
      fontWeight: 500,
      transition: 'all 0.15s ease'
    }}>
                  {label}
                </button>;
  })}
          </div>}
      </div>

      {}
      <div style={{
    display: 'flex',
    alignItems: 'flex-end',
    padding: tokens.space4,
    gap: tokens.space4,
    background: tokens.bgMain,
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusLg,
    marginBottom: tokens.space4,
    flexWrap: 'wrap'
  }}>
        {}
        <div style={{
    flex: 1,
    display: 'flex',
    flexDirection: 'column',
    gap: tokens.space2,
    minWidth: '300px'
  }}>
          {}
          {(selectedDvns.length > 0 || selectedChains.length > 0) && <div style={{
    display: 'flex',
    flexWrap: 'wrap',
    gap: '6px',
    marginBottom: tokens.space1
  }}>
              {selectedDvns.map(dvn => {
    const dvnObj = deploymentsData.find(d => d.dvnDisplayName === dvn);
    return <div key={dvn} style={{
      display: 'flex',
      alignItems: 'center',
      gap: '6px',
      background: isDark ? 'rgba(167, 125, 255, 0.15)' : 'rgba(167, 125, 255, 0.1)',
      padding: '4px 10px',
      borderRadius: tokens.radiusFull,
      border: `1px solid ${isDark ? 'rgba(167, 125, 255, 0.3)' : 'rgba(167, 125, 255, 0.2)'}`
    }}>
                    <DVNIcon dvnKey={dvnObj?.id || dvn} size={16} />
                    <span style={{
      fontSize: '12px',
      color: tokens.textPrimary,
      fontWeight: 500
    }}>{dvn}</span>
                    <button onClick={() => setSelectedDvns(selectedDvns.filter(d => d !== dvn))} style={{
      background: 'none',
      border: 'none',
      cursor: 'pointer',
      padding: 0,
      width: '16px',
      height: '16px',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      color: tokens.textSecondary,
      fontSize: '16px',
      lineHeight: 1
    }}>
                      ×
                    </button>
                  </div>;
  })}
              {selectedChains.map(chain => {
    const chainInfo = uniqueChains.find(c => c.value === chain);
    return <div key={chain} style={{
      display: 'flex',
      alignItems: 'center',
      gap: '6px',
      background: isDark ? 'rgba(167, 125, 255, 0.15)' : 'rgba(167, 125, 255, 0.1)',
      padding: '4px 10px',
      borderRadius: tokens.radiusFull,
      border: `1px solid ${isDark ? 'rgba(167, 125, 255, 0.3)' : 'rgba(167, 125, 255, 0.2)'}`
    }}>
                    <NetworkIcon chainKey={chain} size={16} />
                    <span style={{
      fontSize: '12px',
      color: tokens.textPrimary,
      fontWeight: 500
    }}>{chainInfo?.label || chain}</span>
                    <button onClick={() => setSelectedChains(selectedChains.filter(c => c !== chain))} style={{
      background: 'none',
      border: 'none',
      cursor: 'pointer',
      padding: 0,
      width: '16px',
      height: '16px',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      color: tokens.textSecondary,
      fontSize: '16px',
      lineHeight: 1
    }}>
                      ×
                    </button>
                  </div>;
  })}
            </div>}

          {}
          <div style={{
    display: 'flex',
    gap: tokens.space2
  }}>
            {}
            <div style={{
    position: 'relative',
    flex: 1
  }} ref={networkSearchRef}>
              <input type="text" placeholder="Search networks..." value={networkSearchTerm} onChange={e => {
    setNetworkSearchTerm(e.target.value);
    setIsNetworkSearchOpen(true);
  }} onFocus={() => setIsNetworkSearchOpen(true)} style={{
    width: '100%',
    padding: `${tokens.space2} ${tokens.space3}`,
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusLg,
    background: tokens.bgSecondary,
    color: tokens.textPrimary,
    fontSize: '14px',
    outline: 'none',
    boxSizing: 'border-box'
  }} />
              {isNetworkSearchOpen && uniqueDvnResults.networks.length > 0 && <div style={{
    position: 'absolute',
    top: '100%',
    left: 0,
    right: 0,
    marginTop: tokens.space1,
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusLg,
    background: tokens.bgMain,
    zIndex: 1000,
    boxShadow: tokens.shadowLg,
    maxHeight: '300px',
    overflowY: 'auto',
    padding: `${tokens.space1} 0`
  }}>
                  <div style={{
    padding: '8px 12px 4px',
    fontSize: '11px',
    fontWeight: 600,
    color: tokens.textTertiary,
    textTransform: 'uppercase',
    letterSpacing: '0.5px'
  }}>
                    Networks
                  </div>
                  {uniqueDvnResults.networks.slice(0, 50).map(network => {
    const isSelected = selectedChains.includes(network.chainKey);
    return <div key={network.chainKey} onClick={() => {
      const newSelected = isSelected ? selectedChains.filter(c => c !== network.chainKey) : [...selectedChains, network.chainKey].slice(0, 10);
      setSelectedChains(newSelected);
      setNetworkSearchTerm('');
      setIsNetworkSearchOpen(false);
    }} style={{
      display: 'flex',
      alignItems: 'center',
      gap: '8px',
      padding: '8px 12px',
      cursor: 'pointer',
      background: isSelected ? tokens.bgSecondary : tokens.bgMain,
      color: tokens.textPrimary,
      fontWeight: isSelected ? 500 : 400,
      transition: 'background 0.15s'
    }} onMouseEnter={e => {
      if (!isSelected) e.currentTarget.style.background = tokens.bgSecondary;
    }} onMouseLeave={e => {
      if (!isSelected) e.currentTarget.style.background = tokens.bgMain;
    }}>
                        <NetworkIcon chainKey={network.chainKey} size={18} />
                        <span style={{
      fontSize: '14px'
    }}>{network.chainDisplayName}</span>
                      </div>;
  })}
                </div>}
            </div>

            {}
            <div style={{
    position: 'relative',
    flex: 1
  }} ref={dvnSearchRef}>
              <input type="text" placeholder="Search DVNs..." value={dvnSearchTerm} onChange={e => {
    setDvnSearchTerm(e.target.value);
    setIsDvnSearchOpen(true);
  }} onFocus={() => setIsDvnSearchOpen(true)} style={{
    width: '100%',
    padding: `${tokens.space2} ${tokens.space3}`,
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusLg,
    background: tokens.bgSecondary,
    color: tokens.textPrimary,
    fontSize: '14px',
    outline: 'none',
    boxSizing: 'border-box'
  }} />
              {isDvnSearchOpen && uniqueDvnResults.dvns.length > 0 && <div style={{
    position: 'absolute',
    top: '100%',
    left: 0,
    right: 0,
    marginTop: tokens.space1,
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusLg,
    background: tokens.bgMain,
    zIndex: 1000,
    boxShadow: tokens.shadowLg,
    maxHeight: '300px',
    overflowY: 'auto',
    padding: `${tokens.space1} 0`
  }}>
                  <div style={{
    padding: '8px 12px 4px',
    fontSize: '11px',
    fontWeight: 600,
    color: tokens.textTertiary,
    textTransform: 'uppercase',
    letterSpacing: '0.5px'
  }}>
                    DVNs
                  </div>
                  {uniqueDvnResults.dvns.slice(0, 50).map(deployment => {
    const isSelected = selectedDvns.includes(deployment.dvnDisplayName);
    return <div key={deployment.dvnDisplayName} onClick={() => {
      const newSelected = isSelected ? selectedDvns.filter(d => d !== deployment.dvnDisplayName) : [...selectedDvns, deployment.dvnDisplayName].slice(0, 10);
      setSelectedDvns(newSelected);
      setDvnSearchTerm('');
      setIsDvnSearchOpen(false);
    }} style={{
      display: 'flex',
      alignItems: 'center',
      gap: '8px',
      padding: '8px 12px',
      cursor: 'pointer',
      background: isSelected ? tokens.bgSecondary : tokens.bgMain,
      color: tokens.textPrimary,
      fontWeight: isSelected ? 500 : 400,
      transition: 'background 0.15s'
    }} onMouseEnter={e => {
      if (!isSelected) e.currentTarget.style.background = tokens.bgSecondary;
    }} onMouseLeave={e => {
      if (!isSelected) e.currentTarget.style.background = tokens.bgMain;
    }}>
                        <DVNIcon dvnKey={deployment.id} size={18} />
                        <span style={{
      fontSize: '14px'
    }}>{deployment.dvnDisplayName}</span>
                      </div>;
  })}
                </div>}
            </div>
          </div>
        </div>

        {}
        <div style={{
    display: 'flex',
    gap: tokens.space2,
    flexShrink: 0
  }}>
          <button onClick={handleReset} style={{
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusLg,
    padding: `${tokens.space2} ${tokens.space4}`,
    color: tokens.textPrimary,
    background: tokens.bgMain,
    cursor: 'pointer',
    fontWeight: 500,
    fontSize: '14px',
    transition: 'all 0.15s'
  }} onMouseEnter={e => {
    e.currentTarget.style.borderColor = tokens.accent;
    e.currentTarget.style.color = tokens.accent;
  }} onMouseLeave={e => {
    e.currentTarget.style.borderColor = tokens.divider;
    e.currentTarget.style.color = tokens.textPrimary;
  }}>
            Reset
          </button>
          <a href="https://metadata.layerzero-api.com/v1/metadata/dvns" target="_blank" rel="noopener noreferrer" className="dvn-view-metadata-link" style={{
    color: tokens.accent,
    textDecoration: 'none',
    fontSize: '14px',
    fontWeight: 500,
    display: 'flex',
    alignItems: 'center',
    padding: `${tokens.space2} ${tokens.space3}`,
    border: 'none',
    boxShadow: 'none',
    backgroundImage: 'none'
  }}>
            View Metadata
          </a>
        </div>
      </div>

      {}
      <div className="dvn-table-container dvn-table-wrapper" style={{
    background: tokens.bgMain,
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusLg,
    overflow: 'hidden',
    boxShadow: tokens.shadowSm,
    marginTop: tokens.space4
  }}>
        <div style={{
    overflowX: 'auto'
  }}>
          <table style={{
    width: '100%',
    borderCollapse: 'collapse',
    fontSize: '14px'
  }}>
            <thead>
              <tr>
                <th style={{
    background: tokens.bgSecondary,
    padding: '8px 16px',
    textAlign: 'center',
    fontWeight: 600,
    color: tokens.textPrimary,
    borderBottom: `1px solid ${tokens.divider}`,
    borderRight: `1px solid ${tokens.divider}`,
    whiteSpace: 'nowrap',
    width: '26%'
  }}>
                  DVN
                </th>
                {hasSelectedChains && <th style={{
    background: tokens.bgSecondary,
    padding: '8px 16px',
    textAlign: 'center',
    fontWeight: 600,
    color: tokens.textPrimary,
    borderBottom: `1px solid ${tokens.divider}`,
    borderRight: `1px solid ${tokens.divider}`,
    whiteSpace: 'nowrap',
    width: '16%'
  }}>
                    Coverage
                  </th>}
                <th style={{
    background: tokens.bgSecondary,
    padding: '8px 16px',
    textAlign: 'center',
    fontWeight: 600,
    color: tokens.textPrimary,
    borderBottom: `1px solid ${tokens.divider}`,
    borderRight: `1px solid ${tokens.divider}`,
    whiteSpace: 'nowrap',
    width: '22%'
  }}>
                  Chain
                </th>
                <th style={{
    background: tokens.bgSecondary,
    padding: '8px 16px',
    textAlign: 'center',
    fontWeight: 600,
    color: tokens.textPrimary,
    borderBottom: `1px solid ${tokens.divider}`,
    whiteSpace: 'nowrap',
    width: '36%'
  }}>
                  DVN Address
                </th>
              </tr>
            </thead>
            <tbody>
              {limitedData.length === 0 ? <tr>
                  <td colSpan={hasSelectedChains ? 4 : 3} style={{
    textAlign: 'center',
    padding: tokens.space6,
    color: tokens.textTertiary
  }}>
                    No DVN deployments found.
                  </td>
                </tr> : hasSelectedChains ? limitedData.flatMap(group => group.deployments.map((deployment, index) => <tr key={`${deployment.chainKey}-${deployment.dvnAddress}`} style={{
    background: tokens.bgMain,
    transition: 'background 0.15s'
  }} onMouseEnter={e => e.currentTarget.style.background = tokens.bgSecondary} onMouseLeave={e => e.currentTarget.style.background = tokens.bgMain}>
                      {index === 0 && <>
                          <td rowSpan={group.deployments.length} style={{
    padding: '12px 16px',
    borderBottom: `1px solid ${tokens.divider}`,
    borderRight: `1px solid ${tokens.divider}`,
    verticalAlign: 'middle'
  }}>
                            <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: '8px'
  }}>
                              <DVNIcon dvnKey={deployment.id} size={18} />
                              <span style={{
    fontWeight: 500,
    color: tokens.textPrimary,
    fontSize: '13px'
  }}>{group.dvnDisplayName}</span>
                              <CopyButton text={group.dvnDisplayName} />
                            </div>
                          </td>
                          <td rowSpan={group.deployments.length} style={{
    padding: '12px 16px',
    borderBottom: `1px solid ${tokens.divider}`,
    borderRight: `1px solid ${tokens.divider}`,
    verticalAlign: 'middle',
    color: tokens.textSecondary,
    fontWeight: 500,
    fontSize: '13px'
  }}>
                            {group.coverage}
                          </td>
                        </>}
                      <td style={{
    padding: '12px 16px',
    borderBottom: `1px solid ${tokens.divider}`,
    borderRight: `1px solid ${tokens.divider}`,
    verticalAlign: 'middle'
  }}>
                        <a href={`/v2/deployments/chains/${deployment.chainKey}`} style={{
    display: 'flex',
    alignItems: 'center',
    gap: '8px',
    color: tokens.textPrimary,
    textDecoration: 'none',
    border: 'none',
    boxShadow: 'none',
    backgroundImage: 'none'
  }} onMouseEnter={e => e.currentTarget.querySelector('span').style.color = tokens.accent} onMouseLeave={e => e.currentTarget.querySelector('span').style.color = tokens.textPrimary}>
                          <NetworkIcon chainKey={deployment.chainKey} size={18} />
                          <span style={{
    fontWeight: 500,
    transition: 'color 0.15s',
    fontSize: '13px',
    textDecoration: 'none'
  }}>{deployment.chainDisplayName}</span>
                        </a>
                      </td>
                      <td style={{
    padding: '12px 16px',
    borderBottom: `1px solid ${tokens.divider}`,
    verticalAlign: 'middle'
  }}>
                        <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: '8px'
  }}>
                          <a href={`${getExplorerUrl(deployment)}/${deployment.dvnAddress}`} target="_blank" rel="noopener noreferrer" style={{
    color: tokens.textSecondary,
    textDecoration: 'none',
    fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
    fontSize: '12px',
    border: 'none',
    boxShadow: 'none',
    backgroundImage: 'none'
  }} onMouseEnter={e => {
    e.currentTarget.style.color = tokens.accent;
  }} onMouseLeave={e => {
    e.currentTarget.style.color = tokens.textSecondary;
  }}>
                            {deployment.dvnAddress.slice(0, 25)}...{deployment.dvnAddress.slice(-6)}
                          </a>
                          <CopyButton text={deployment.dvnAddress} />
                        </div>
                      </td>
                    </tr>)) : limitedData.flatMap(group => group.deployments.map(deployment => <tr key={`${deployment.chainKey}-${deployment.dvnAddress}`} style={{
    background: tokens.bgMain,
    transition: 'background 0.15s'
  }} onMouseEnter={e => e.currentTarget.style.background = tokens.bgSecondary} onMouseLeave={e => e.currentTarget.style.background = tokens.bgMain}>
                      <td style={{
    padding: '12px 16px',
    borderBottom: `1px solid ${tokens.divider}`,
    borderRight: `1px solid ${tokens.divider}`,
    verticalAlign: 'middle'
  }}>
                        <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: '8px'
  }}>
                          <DVNIcon dvnKey={deployment.id} size={18} />
                          <span style={{
    fontWeight: 500,
    color: tokens.textPrimary,
    fontSize: '13px'
  }}>{deployment.dvnDisplayName}</span>
                          <CopyButton text={deployment.dvnDisplayName} />
                        </div>
                      </td>
                      <td style={{
    padding: '12px 16px',
    borderBottom: `1px solid ${tokens.divider}`,
    borderRight: `1px solid ${tokens.divider}`,
    verticalAlign: 'middle'
  }}>
                        <a href={`/v2/deployments/chains/${deployment.chainKey}`} style={{
    display: 'flex',
    alignItems: 'center',
    gap: '8px',
    color: tokens.textPrimary,
    textDecoration: 'none',
    border: 'none',
    boxShadow: 'none',
    backgroundImage: 'none'
  }} onMouseEnter={e => e.currentTarget.querySelector('span').style.color = tokens.accent} onMouseLeave={e => e.currentTarget.querySelector('span').style.color = tokens.textPrimary}>
                          <NetworkIcon chainKey={deployment.chainKey} size={18} />
                          <span style={{
    fontWeight: 500,
    transition: 'color 0.15s',
    fontSize: '13px',
    textDecoration: 'none'
  }}>{deployment.chainDisplayName}</span>
                        </a>
                      </td>
                      <td style={{
    padding: '12px 16px',
    borderBottom: `1px solid ${tokens.divider}`,
    verticalAlign: 'middle'
  }}>
                        <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: '8px'
  }}>
                          <a href={`${getExplorerUrl(deployment)}/${deployment.dvnAddress}`} target="_blank" rel="noopener noreferrer" style={{
    color: tokens.textSecondary,
    textDecoration: 'none',
    fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
    fontSize: '12px',
    border: 'none',
    boxShadow: 'none',
    backgroundImage: 'none'
  }} onMouseEnter={e => {
    e.currentTarget.style.color = tokens.accent;
  }} onMouseLeave={e => {
    e.currentTarget.style.color = tokens.textSecondary;
  }}>
                            {deployment.dvnAddress.slice(0, 25)}...{deployment.dvnAddress.slice(-6)}
                          </a>
                          <CopyButton text={deployment.dvnAddress} />
                        </div>
                      </td>
                    </tr>))}
            </tbody>
          </table>
        </div>
        {}
        {hasMoreData && <div style={{
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    padding: tokens.space4,
    borderTop: `1px solid ${tokens.divider}`,
    gap: tokens.space3
  }}>
            <span style={{
    color: tokens.textSecondary,
    fontSize: '13px'
  }}>
              Showing {displayedRowCount} of {totalRowCount} rows
            </span>
            <button onClick={() => setDisplayLimit(prev => prev + 50)} style={{
    padding: `${tokens.space2} ${tokens.space4}`,
    background: tokens.accent,
    color: 'white',
    border: 'none',
    borderRadius: tokens.radiusMd,
    cursor: 'pointer',
    fontWeight: 500,
    fontSize: '13px',
    transition: 'background 0.15s'
  }} onMouseEnter={e => e.currentTarget.style.background = tokens.accentHover} onMouseLeave={e => e.currentTarget.style.background = tokens.accent}>
              Load more
            </button>
            <button onClick={() => setDisplayLimit(totalRowCount)} style={{
    padding: `${tokens.space2} ${tokens.space4}`,
    background: 'transparent',
    color: tokens.textSecondary,
    border: `1px solid ${tokens.divider}`,
    borderRadius: tokens.radiusMd,
    cursor: 'pointer',
    fontWeight: 500,
    fontSize: '13px',
    transition: 'all 0.15s'
  }} onMouseEnter={e => {
    e.currentTarget.style.borderColor = tokens.accent;
    e.currentTarget.style.color = tokens.accent;
  }} onMouseLeave={e => {
    e.currentTarget.style.borderColor = tokens.divider;
    e.currentTarget.style.color = tokens.textSecondary;
  }}>
              Show all
            </button>
          </div>}
      </div>
    </div>;
};

Seamlessly set up and configure your application's **Security Stack** to include the following Decentralized Verifier Networks (DVNs). To successfully add a DVN to verify a pathway, that DVN must be deployed on both chains!

<br />

<DvnAddressesTable />

### Next Steps

Add these DVNs to your OApp configuration by following the CLI Guide or an appropriate quickstart:

* [CLI Guide](../get-started/create-lz-oapp/start)
* [OApp Quickstart](../developers/evm/oapp/overview)
* [OFT Quickstart](../developers/evm/oft/quickstart)
* [lzRead Quickstart](../developers/evm/lzread/overview)
