Skip to main content
DSA Connect supports multiple EVM-compatible chains, allowing you to deploy and interact with DeFi protocols across different networks.

Supported Chains

DSA Connect currently supports the following chains:

Ethereum

Chain ID: 1Mainnet with full protocol support

Polygon

Chain ID: 137Low-cost alternative to Ethereum

Arbitrum

Chain ID: 42161Layer 2 scaling solution

Optimism

Chain ID: 10Optimistic rollup L2

Avalanche

Chain ID: 43114High-performance blockchain

Fantom

Chain ID: 250Fast and low-cost network

Base

Chain ID: 8453Coinbase’s L2 network

BSC

Chain ID: 56Binance Smart Chain

Plasma

Chain ID: 9745Plasma network

Initializing on Different Chains

Browser Setup

import { DSA } from 'dsa-connect'
import Web3 from 'web3'

const web3 = new Web3(window.ethereum)
const dsa = new DSA(web3, 1) // Chain ID 1

Node.js Setup

const Web3 = require('web3')
const DSA = require('dsa-connect')

const web3 = new Web3(
  new Web3.providers.HttpProvider('https://eth-mainnet.alchemyapi.io/v2/YOUR-KEY')
)

const dsa = new DSA({
  web3: web3,
  mode: 'node',
  privateKey: process.env.PRIVATE_KEY
}, 1)

Chain Validation

By default, DSA Connect validates that the Web3 provider’s chain ID matches the specified chain:
const dsa = new DSA(web3, 137) // Will throw error if Web3 is not on Polygon
To skip validation (use with caution):
const dsa = new DSA(
  web3,
  137,
  { skipChainIdValidation: true }
)
Skipping chain ID validation can lead to transactions being sent to the wrong network. Only use this option if you’re certain about the network configuration.

Checking Current Chain

Get the current chain ID from your DSA instance:
console.log('Current Chain ID:', dsa.instance.chainId)

Switching Networks

When users switch networks in their wallet:
1

Detect Network Change

// Listen for network changes
if (window.ethereum) {
  window.ethereum.on('chainChanged', async (chainId) => {
    console.log('Network changed to:', chainId)
    // Reload the page or reinitialize
  })
}
2

Refresh Chain ID

// Refresh the chain ID in DSA
await dsa.refreshChainId()
console.log('Updated to chain:', dsa.instance.chainId)
3

Reinitialize if Needed

// If chain is not supported, reinitialize
const supportedChains = [1, 137, 42161, 43114, 10, 250, 8453, 9745, 56]
const currentChainId = await web3.eth.getChainId()

if (!supportedChains.includes(currentChainId)) {
  console.error('Unsupported chain')
  // Prompt user to switch network
} else {
  // Reinitialize DSA with new chain
  dsa = new DSA(web3, currentChainId)
}

Multi-Chain Account Management

Separate Accounts per Chain

DSA accounts are chain-specific. The same Ethereum address can have different DSA accounts on each chain:
// Get accounts on Ethereum
const ethDsa = new DSA(web3Eth, 1)
const ethAccounts = await ethDsa.getAccounts(userAddress)

// Get accounts on Polygon
const polygonDsa = new DSA(web3Polygon, 137)
const polygonAccounts = await polygonDsa.getAccounts(userAddress)

console.log('Ethereum DSAs:', ethAccounts.length)
console.log('Polygon DSAs:', polygonAccounts.length)

Create Account on Multiple Chains

async function createAccountOnAllChains(userAddress) {
  const chains = [
    { id: 1, name: 'Ethereum', rpc: 'https://eth-mainnet...' },
    { id: 137, name: 'Polygon', rpc: 'https://polygon-rpc.com' },
    { id: 42161, name: 'Arbitrum', rpc: 'https://arb1.arbitrum.io/rpc' },
  ]

  for (const chain of chains) {
    const web3 = new Web3(new Web3.providers.HttpProvider(chain.rpc))
    const dsa = new DSA({ web3, mode: 'node', privateKey: PRIVATE_KEY }, chain.id)

    const accounts = await dsa.getAccounts(userAddress)

    if (accounts.length === 0) {
      console.log(`Creating DSA on ${chain.name}...`)
      await dsa.build()
      console.log(`✓ Created DSA on ${chain.name}`)
    } else {
      console.log(`✓ DSA already exists on ${chain.name}`)
    }
  }
}

Chain-Specific Considerations

Gas Prices

Different chains have different gas price dynamics:
// Ethereum: High gas, use EIP-1559
const spell = dsa.Spell()
spell.add({ /* ... */ })

await spell.cast({
  maxFeePerGas: '50000000000', // 50 gwei
  maxPriorityFeePerGas: '2000000000' // 2 gwei tip
})

Protocol Availability

Not all protocols are available on all chains. Check connector availability:
// Ethereum has the most protocols
const ethDsa = new DSA(web3, 1)
const ethSpell = ethDsa.Spell()
ethSpell.add({
  connector: 'aave',
  method: 'deposit',
  args: [/* ... */]
})

// Some protocols might not be on other chains
const polygonDsa = new DSA(web3, 137)
const polygonSpell = polygonDsa.Spell()
// Always verify protocol exists on target chain
polygonSpell.add({
  connector: 'aave', // Available on Polygon
  method: 'deposit',
  args: [/* ... */]
})
See the Connectors Reference for chain-specific protocol availability.

Cross-Chain Example Application

Multi-Chain Portfolio Manager

class MultiChainDSA {
  constructor() {
    this.chains = {
      ethereum: { id: 1, dsa: null },
      polygon: { id: 137, dsa: null },
      arbitrum: { id: 42161, dsa: null }
    }
  }

  async initialize(web3Providers, privateKey) {
    for (const [name, config] of Object.entries(this.chains)) {
      const web3 = new Web3(web3Providers[name])
      config.dsa = new DSA(
        { web3, mode: 'node', privateKey },
        config.id
      )
    }
  }

  async getAccountsAllChains(userAddress) {
    const results = {}

    for (const [name, config] of Object.entries(this.chains)) {
      results[name] = await config.dsa.getAccounts(userAddress)
    }

    return results
  }

  async depositOnChain(chainName, amount) {
    const dsa = this.chains[chainName].dsa

    const spell = dsa.Spell()
    spell.add({
      connector: 'aave',
      method: 'deposit',
      args: ['0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', amount, 0, 0]
    })

    return await spell.cast()
  }

  async getTotalValueAllChains(userAddress) {
    let totalValue = 0

    for (const [name, config] of Object.entries(this.chains)) {
      const accounts = await config.dsa.getAccounts(userAddress)

      for (const account of accounts) {
        // Fetch balance logic here
        const balance = await this.getAccountBalance(config.dsa, account)
        totalValue += balance
      }
    }

    return totalValue
  }

  async getAccountBalance(dsa, account) {
    // Implement balance fetching logic
    return 0
  }
}

// Usage
const multiChainDSA = new MultiChainDSA()

await multiChainDSA.initialize(
  {
    ethereum: 'https://eth-mainnet.alchemyapi.io/v2/KEY',
    polygon: 'https://polygon-rpc.com',
    arbitrum: 'https://arb1.arbitrum.io/rpc'
  },
  process.env.PRIVATE_KEY
)

const accounts = await multiChainDSA.getAccountsAllChains(userAddress)
console.log('Accounts per chain:', accounts)

// Deposit on Polygon
await multiChainDSA.depositOnChain('polygon', '1000000000000000000')

Network Switching in Browser

Request Network Switch

async function switchToNetwork(chainId) {
  const chainParams = {
    1: {
      chainId: '0x1',
      chainName: 'Ethereum Mainnet'
    },
    137: {
      chainId: '0x89',
      chainName: 'Polygon',
      rpcUrls: ['https://polygon-rpc.com'],
      nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 },
      blockExplorerUrls: ['https://polygonscan.com']
    },
    42161: {
      chainId: '0xa4b1',
      chainName: 'Arbitrum One',
      rpcUrls: ['https://arb1.arbitrum.io/rpc'],
      nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
      blockExplorerUrls: ['https://arbiscan.io']
    }
  }

  try {
    await window.ethereum.request({
      method: 'wallet_switchEthereumChain',
      params: [{ chainId: chainParams[chainId].chainId }]
    })
  } catch (error) {
    // Chain not added, try to add it
    if (error.code === 4902) {
      await window.ethereum.request({
        method: 'wallet_addEthereumChain',
        params: [chainParams[chainId]]
      })
    }
  }
}

// Switch to Polygon
await switchToNetwork(137)

RPC Endpoints

Recommended RPC endpoints for each chain:
ChainPublic RPCRecommended Provider
Ethereum (1)https://cloudflare-eth.comAlchemy, Infura
Polygon (137)https://polygon-rpc.comAlchemy, QuickNode
Arbitrum (42161)https://arb1.arbitrum.io/rpcAlchemy, Infura
Optimism (10)https://mainnet.optimism.ioAlchemy, QuickNode
Avalanche (43114)https://api.avax.network/ext/bc/C/rpcInfura
Fantom (250)https://rpc.ftm.toolsAnkr
Base (8453)https://mainnet.base.orgAlchemy, QuickNode
BSC (56)https://bsc-dataseed.binance.orgAnkr, QuickNode
Public RPCs may have rate limits. For production applications, use dedicated RPC providers like Alchemy, Infura, or QuickNode.

Best Practices

Always verify you’re on the correct chain:
const currentChainId = await web3.eth.getChainId()
if (currentChainId !== expectedChainId) {
  console.error('Wrong network!')
  // Prompt user to switch
  await switchToNetwork(expectedChainId)
}
Listen for and handle network changes:
window.ethereum.on('chainChanged', async (chainId) => {
  // Reload or reinitialize
  window.location.reload()
})
Remember that token addresses differ across chains:
const USDC = {
  1: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  137: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',
  42161: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8'
}

const usdcAddress = USDC[dsa.instance.chainId]
Optimize gas strategies per chain:
function getGasConfig(chainId) {
  switch(chainId) {
    case 1: // Ethereum - be careful with gas
      return { maxFeePerGas: '50000000000' }
    case 137: // Polygon - gas is cheap
      return { maxFeePerGas: '100000000000' }
    case 42161: // Arbitrum - very cheap
      return { gasPrice: '100000000' }
    default:
      return {}
  }
}

await spell.cast(getGasConfig(dsa.instance.chainId))
Keep separate DSA instances for each chain:
const instances = {
  eth: new DSA(web3Eth, 1),
  polygon: new DSA(web3Polygon, 137),
  arbitrum: new DSA(web3Arbitrum, 42161)
}

// Use the appropriate instance
await instances.polygon.build()

Next Steps

Connectors Reference

See which protocols are available on each chain

API Reference

Complete API documentation

Build docs developers (and LLMs) love