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 →
Parameter | Type | Default | Description |
---|---|---|---|
ario | ARIO | Required | AR.IO SDK instance (mainnet/testnet) |
minStake | number | 0 | Minimum stake required (in mIO tokens) |
maxGateways | number | 100 | Maximum number of gateways to return |
includeOperatorStake | boolean | true | Include operator stake in calculations |
includeDelegatedStake | boolean | true | Include delegated stake in calculations |
sortBy | string | 'stake' | Sort order for gateway selection |
filterCriteria | object | Additional 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
- Set Appropriate Stake Requirements: Use
minStake
to filter for reliable gateways - Limit Gateway Count: Use
maxGateways
to prevent overwhelming routing strategies - Use Caching: Wrap with
SimpleCacheGatewaysProvider
for production - Handle Network Failures: Implement proper error handling and retries
- Monitor Performance: Track discovery times and success rates
- Use Mainnet for Production: Only use testnet for development and testing
- Consider Geographic Distribution: Filter gateways based on user location
- Implement Fallbacks: Have backup providers for when network discovery fails
Comparison with Other Providers
← Swipe to see more →
Feature | NetworkGatewaysProvider | StaticGatewaysProvider | SimpleCacheGatewaysProvider |
---|---|---|---|
Discovery | Dynamic from network | Predefined list | Cached from wrapped provider |
Performance | Medium (network calls) | High (no network) | High (cached) |
Reliability | High (real network data) | Medium (static data) | High (cached + fallback) |
Maintenance | Low (automatic updates) | High (manual updates) | Low (automatic + cached) |
Use Case | Production applications | Development/Testing | Production (performance) |
← Swipe to see more →
Related Documentation
- Gateway Providers Overview: Compare all gateway providers
- StaticGatewaysProvider: Static gateway configuration
- SimpleCacheGatewaysProvider: Caching wrapper
- Wayfinder Configuration: Main wayfinder setup
- Routing Strategies: How gateways are selected