NetworkGatewaysProvider

Overview

The NetworkGatewaysProvider discovers AR.IO gateways from the AR.IO Network using the AR.IO SDK. It provides dynamic access to the full network of verified gateways, making it the recommended choice for production applications.

Installation

npm install @ar.io/wayfinder-core

Basic Usage

import { NetworkGatewaysProvider } from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

const provider = new NetworkGatewaysProvider({
  ario: ARIO.mainnet(),
})

const gateways = await provider.getGateways()
console.log('Available gateways:', gateways)

Configuration Options

NetworkGatewaysProviderOptions

interface NetworkGatewaysProviderOptions {
  ario: ARIO
  minStake?: number
  maxGateways?: number
  includeOperatorStake?: boolean
  includeDelegatedStake?: boolean
  sortBy?: 'stake' | 'performance' | 'random'
  filterCriteria?: GatewayFilterCriteria
}

Parameters

← Swipe to see more →
ParameterTypeDefaultDescription
arioARIORequiredAR.IO SDK instance (mainnet/testnet)
minStakenumber0Minimum stake required (in mIO tokens)
maxGatewaysnumber100Maximum number of gateways to return
includeOperatorStakebooleantrueInclude operator stake in calculations
includeDelegatedStakebooleantrueInclude delegated stake in calculations
sortBystring'stake'Sort order for gateway selection
filterCriteriaobjectAdditional filtering criteria
← Swipe to see more →

Configuration Examples

Production Configuration

import { NetworkGatewaysProvider } from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

// High-reliability production setup
const provider = new NetworkGatewaysProvider({
  ario: ARIO.mainnet(),
  minStake: 100000, // 100k mIO minimum stake
  maxGateways: 20, // Limit to top 20 gateways
  includeOperatorStake: true,
  includeDelegatedStake: true,
  sortBy: 'stake',
})

Development Configuration

import { NetworkGatewaysProvider } from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

// Development setup with more gateways
const provider = new NetworkGatewaysProvider({
  ario: ARIO.testnet(), // Use testnet for development
  minStake: 1000, // Lower stake requirement
  maxGateways: 50, // More gateways for testing
  sortBy: 'random', // Random selection for testing
})

Performance-Optimized Configuration

import { NetworkGatewaysProvider } from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

// Optimized for performance
const provider = new NetworkGatewaysProvider({
  ario: ARIO.mainnet(),
  minStake: 250000, // Very high stake requirement
  maxGateways: 10, // Limit to top 10 gateways
  includeOperatorStake: true,
  includeDelegatedStake: true,
  sortBy: 'performance', // Sort by performance metrics
  filterCriteria: {
    minUptime: 0.99, // 99% uptime requirement
    maxLatency: 100, // Max 100ms latency
  },
})

Advanced Usage

Custom Filtering

import { NetworkGatewaysProvider } from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

class FilteredNetworkGatewaysProvider extends NetworkGatewaysProvider {
  constructor(options) {
    super(options)
    this.customFilters = options.customFilters || []
  }

  async getGateways() {
    let gateways = await super.getGateways()

    // Apply custom filters
    for (const filter of this.customFilters) {
      gateways = await filter(gateways)
    }

    return gateways
  }
}

// Usage with custom filters
const provider = new FilteredNetworkGatewaysProvider({
  ario: ARIO.mainnet(),
  minStake: 50000,
  customFilters: [
    // Filter by geographic region
    async (gateways) => {
      const geoData = await getGatewayGeographicData(gateways)
      return gateways.filter(
        (gateway, index) => geoData[index].region === 'us-east',
      )
    },
    // Filter by health status
    async (gateways) => {
      const healthChecks = await Promise.allSettled(
        gateways.map((gateway) => checkGatewayHealth(gateway)),
      )
      return gateways.filter(
        (gateway, index) =>
          healthChecks[index].status === 'fulfilled' &&
          healthChecks[index].value.healthy,
      )
    },
  ],
})

Caching with Refresh Strategy

import {
  NetworkGatewaysProvider,
  SimpleCacheGatewaysProvider,
} from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

class SmartCacheProvider extends SimpleCacheGatewaysProvider {
  constructor(options) {
    super({
      gatewaysProvider: new NetworkGatewaysProvider(options.networkOptions),
      ttlSeconds: options.ttlSeconds || 300, // 5 minutes
      maxCacheSize: options.maxCacheSize || 100,
    })

    this.refreshInterval = options.refreshInterval || 60000 // 1 minute
    this.lastRefreshAttempt = 0
    this.isRefreshing = false
  }

  async getGateways() {
    const now = Date.now()

    // Attempt background refresh if needed
    if (
      now - this.lastRefreshAttempt > this.refreshInterval &&
      !this.isRefreshing
    ) {
      this.backgroundRefresh()
    }

    return super.getGateways()
  }

  async backgroundRefresh() {
    this.isRefreshing = true
    this.lastRefreshAttempt = Date.now()

    try {
      await this.refresh()
      console.log('Gateway cache refreshed successfully')
    } catch (error) {
      console.warn('Background refresh failed:', error.message)
    } finally {
      this.isRefreshing = false
    }
  }
}

// Usage
const smartProvider = new SmartCacheProvider({
  networkOptions: {
    ario: ARIO.mainnet(),
    minStake: 50000,
    maxGateways: 25,
  },
  ttlSeconds: 300, // 5 minute cache
  refreshInterval: 60000, // Try refresh every minute
  maxCacheSize: 100,
})

Multi-Network Provider

import { NetworkGatewaysProvider } from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

class MultiNetworkGatewaysProvider {
  constructor(options) {
    this.providers = [
      new NetworkGatewaysProvider({
        ario: ARIO.mainnet(),
        ...options.mainnetOptions,
      }),
      new NetworkGatewaysProvider({
        ario: ARIO.testnet(),
        ...options.testnetOptions,
      }),
    ]
    this.preferMainnet = options.preferMainnet !== false
  }

  async getGateways() {
    const results = await Promise.allSettled(
      this.providers.map((provider) => provider.getGateways()),
    )

    const allGateways = []

    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        const networkType = index === 0 ? 'mainnet' : 'testnet'
        const gateways = result.value.map((gateway) => ({
          url: gateway,
          network: networkType,
          priority: this.preferMainnet && networkType === 'mainnet' ? 1 : 2,
        }))
        allGateways.push(...gateways)
      }
    })

    // Sort by priority (mainnet first if preferred)
    allGateways.sort((a, b) => a.priority - b.priority)

    return allGateways.map((gateway) => gateway.url)
  }

  async refresh() {
    await Promise.allSettled(
      this.providers.map((provider) => provider.refresh?.()),
    )
  }
}

// Usage
const multiProvider = new MultiNetworkGatewaysProvider({
  preferMainnet: true,
  mainnetOptions: {
    minStake: 100000,
    maxGateways: 15,
  },
  testnetOptions: {
    minStake: 1000,
    maxGateways: 5,
  },
})

Error Handling

Common Errors

import { NetworkGatewaysProvider } from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

const provider = new NetworkGatewaysProvider({
  ario: ARIO.mainnet(),
  minStake: 50000,
})

try {
  const gateways = await provider.getGateways()
  console.log('Found', gateways.length, 'gateways')
} catch (error) {
  switch (error.constructor.name) {
    case 'NetworkError':
      console.error('Failed to connect to AR.IO network')
      console.error('Check internet connection and AR.IO network status')
      break

    case 'NoGatewaysFoundError':
      console.error('No gateways found matching criteria')
      console.error('Consider lowering minStake or increasing maxGateways')
      break

    case 'InvalidConfigurationError':
      console.error('Invalid provider configuration')
      console.error('Check ARIO instance and configuration parameters')
      break

    case 'TimeoutError':
      console.error('Gateway discovery timed out')
      console.error('Network may be slow or unavailable')
      break

    case 'RateLimitError':
      console.error('Rate limit exceeded')
      console.error('Too many requests to AR.IO network')
      break

    default:
      console.error('Unknown error:', error.message)
  }
}

Retry Logic

class RetryableNetworkGatewaysProvider extends NetworkGatewaysProvider {
  constructor(options) {
    super(options)
    this.maxRetries = options.maxRetries || 3
    this.retryDelay = options.retryDelay || 1000
    this.backoffMultiplier = options.backoffMultiplier || 2
  }

  async getGateways() {
    let lastError
    let delay = this.retryDelay

    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        return await super.getGateways()
      } catch (error) {
        lastError = error

        if (attempt === this.maxRetries) {
          break
        }

        console.warn(
          `Gateway discovery attempt ${attempt + 1} failed:`,
          error.message,
        )
        console.warn(`Retrying in ${delay}ms...`)

        await new Promise((resolve) => setTimeout(resolve, delay))
        delay *= this.backoffMultiplier
      }
    }

    throw new Error(
      `Failed to discover gateways after ${this.maxRetries + 1} attempts: ${lastError.message}`,
    )
  }
}

// Usage
const retryableProvider = new RetryableNetworkGatewaysProvider({
  ario: ARIO.mainnet(),
  minStake: 50000,
  maxRetries: 3,
  retryDelay: 1000,
  backoffMultiplier: 2,
})

Performance Optimization

Parallel Discovery

import { NetworkGatewaysProvider } from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

class ParallelNetworkGatewaysProvider {
  constructor(options) {
    this.baseOptions = options
    this.partitionSize = options.partitionSize || 25
  }

  async getGateways() {
    const totalGateways = this.baseOptions.maxGateways || 100
    const partitions = Math.ceil(totalGateways / this.partitionSize)

    const providers = Array.from(
      { length: partitions },
      (_, i) =>
        new NetworkGatewaysProvider({
          ...this.baseOptions,
          maxGateways: Math.min(
            this.partitionSize,
            totalGateways - i * this.partitionSize,
          ),
          offset: i * this.partitionSize,
        }),
    )

    const results = await Promise.allSettled(
      providers.map((provider) => provider.getGateways()),
    )

    const allGateways = []
    results.forEach((result) => {
      if (result.status === 'fulfilled') {
        allGateways.push(...result.value)
      }
    })

    // Remove duplicates and sort by stake
    const uniqueGateways = [...new Set(allGateways)]
    return uniqueGateways.slice(0, totalGateways)
  }
}

// Usage
const parallelProvider = new ParallelNetworkGatewaysProvider({
  ario: ARIO.mainnet(),
  minStake: 50000,
  maxGateways: 100,
  partitionSize: 25,
})

Monitoring and Metrics

class MonitoredNetworkGatewaysProvider extends NetworkGatewaysProvider {
  constructor(options) {
    super(options)
    this.metrics = {
      requestCount: 0,
      totalResponseTime: 0,
      errorCount: 0,
      lastSuccessfulUpdate: null,
      gatewayCount: 0,
    }
  }

  async getGateways() {
    const startTime = Date.now()
    this.metrics.requestCount++

    try {
      const gateways = await super.getGateways()

      this.metrics.totalResponseTime += Date.now() - startTime
      this.metrics.lastSuccessfulUpdate = new Date()
      this.metrics.gatewayCount = gateways.length

      console.log(
        `Discovered ${gateways.length} gateways in ${Date.now() - startTime}ms`,
      )

      return gateways
    } catch (error) {
      this.metrics.errorCount++
      console.error('Gateway discovery failed:', error.message)
      throw error
    }
  }

  getMetrics() {
    return {
      ...this.metrics,
      averageResponseTime:
        this.metrics.totalResponseTime / this.metrics.requestCount,
      successRate:
        (this.metrics.requestCount - this.metrics.errorCount) /
        this.metrics.requestCount,
    }
  }

  resetMetrics() {
    this.metrics = {
      requestCount: 0,
      totalResponseTime: 0,
      errorCount: 0,
      lastSuccessfulUpdate: null,
      gatewayCount: 0,
    }
  }
}

// Usage
const monitoredProvider = new MonitoredNetworkGatewaysProvider({
  ario: ARIO.mainnet(),
  minStake: 50000,
})

// Later, get metrics
console.log('Provider metrics:', monitoredProvider.getMetrics())

Testing

Unit Tests

import { NetworkGatewaysProvider } from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

describe('NetworkGatewaysProvider', () => {
  let mockArio
  let provider

  beforeEach(() => {
    mockArio = {
      gateways: {
        getGateways: jest.fn(),
      },
    }

    provider = new NetworkGatewaysProvider({
      ario: mockArio,
      minStake: 10000,
      maxGateways: 10,
    })
  })

  test('should return gateways from AR.IO network', async () => {
    const mockGateways = [
      { fqdn: 'gateway1.com', stake: 50000 },
      { fqdn: 'gateway2.com', stake: 75000 },
    ]

    mockArio.gateways.getGateways.mockResolvedValue(mockGateways)

    const result = await provider.getGateways()

    expect(result).toEqual([
      'https://gateway2.com', // Higher stake first
      'https://gateway1.com',
    ])
  })

  test('should filter by minimum stake', async () => {
    const mockGateways = [
      { fqdn: 'gateway1.com', stake: 5000 }, // Below minimum
      { fqdn: 'gateway2.com', stake: 15000 }, // Above minimum
    ]

    mockArio.gateways.getGateways.mockResolvedValue(mockGateways)

    const result = await provider.getGateways()

    expect(result).toEqual(['https://gateway2.com'])
  })

  test('should limit number of gateways', async () => {
    const mockGateways = Array.from({ length: 20 }, (_, i) => ({
      fqdn: `gateway${i}.com`,
      stake: 50000 + i * 1000,
    }))

    mockArio.gateways.getGateways.mockResolvedValue(mockGateways)

    const result = await provider.getGateways()

    expect(result).toHaveLength(10) // maxGateways limit
  })

  test('should handle network errors', async () => {
    mockArio.gateways.getGateways.mockRejectedValue(
      new Error('Network connection failed'),
    )

    await expect(provider.getGateways()).rejects.toThrow(
      'Network connection failed',
    )
  })
})

Integration Tests

import { NetworkGatewaysProvider } from '@ar.io/wayfinder-core'
import { ARIO } from '@ar.io/sdk'

describe('NetworkGatewaysProvider Integration', () => {
  test('should discover real gateways from mainnet', async () => {
    const provider = new NetworkGatewaysProvider({
      ario: ARIO.mainnet(),
      minStake: 10000,
      maxGateways: 5,
    })

    const gateways = await provider.getGateways()

    expect(gateways).toBeInstanceOf(Array)
    expect(gateways.length).toBeGreaterThan(0)
    expect(gateways.length).toBeLessThanOrEqual(5)

    // Check that all returned values are valid URLs
    gateways.forEach((gateway) => {
      expect(() => new URL(gateway)).not.toThrow()
    })
  }, 30000) // 30 second timeout for network call

  test('should handle testnet discovery', async () => {
    const provider = new NetworkGatewaysProvider({
      ario: ARIO.testnet(),
      maxGateways: 3,
    })

    const gateways = await provider.getGateways()

    expect(gateways).toBeInstanceOf(Array)
    // Testnet may have fewer gateways
    expect(gateways.length).toBeLessThanOrEqual(3)
  }, 30000)
})

Best Practices

  1. Set Appropriate Stake Requirements: Use minStake to filter for reliable gateways
  2. Limit Gateway Count: Use maxGateways to prevent overwhelming routing strategies
  3. Use Caching: Wrap with SimpleCacheGatewaysProvider for production
  4. Handle Network Failures: Implement proper error handling and retries
  5. Monitor Performance: Track discovery times and success rates
  6. Use Mainnet for Production: Only use testnet for development and testing
  7. Consider Geographic Distribution: Filter gateways based on user location
  8. Implement Fallbacks: Have backup providers for when network discovery fails

Comparison with Other Providers

← Swipe to see more →
FeatureNetworkGatewaysProviderStaticGatewaysProviderSimpleCacheGatewaysProvider
DiscoveryDynamic from networkPredefined listCached from wrapped provider
PerformanceMedium (network calls)High (no network)High (cached)
ReliabilityHigh (real network data)Medium (static data)High (cached + fallback)
MaintenanceLow (automatic updates)High (manual updates)Low (automatic + cached)
Use CaseProduction applicationsDevelopment/TestingProduction (performance)
← Swipe to see more →

Was this page helpful?