Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Multi-chain

Read or transact on Ethereum and Base in the same process. The SDK is per-chain — construct one ApyxClient per chain, share the rest of your application state.

const eth  = createApyxClient({ chain: mainnet, transport: http(ethRpc) });
const base = createApyxClient({ chain: baseChain, transport: http(baseRpc) });

Two clients, one process

import { createApyxClient } from '@koed_jang/apyx-sdk';
import { http, formatUnits } from 'viem';
import { mainnet, base } from 'viem/chains';

const eth = createApyxClient({
  chain: mainnet,
  transport: http(process.env.ETH_RPC_URL),
});

const baseClient = createApyxClient({
  chain: base,
  transport: http(process.env.BASE_RPC_URL),
});

const [ethRate, baseRate] = await Promise.all([
  eth.apyUSD.exchangeRate(),
  baseClient.apyUSD.exchangeRate(),
]);

console.log({
  ethereum: formatUnits(ethRate, 18),
  base:     formatUnits(baseRate, 18),
});

The two clients share nothing at runtime — different chain, different transport, different addresses. Each one fails fast on its own with UnsupportedChainError if you mistakenly pass the wrong viem chain to the wrong constructor.

Address book is per-chain

eth.addresses.apxUSD          // 0x98A878b1Cd98131B271883B390f68D2c90674665
baseClient.addresses.apxUSD   // 0xD993935E13851dd7517af10687EC7e5022127228

The Ethereum and Base apxUSD contracts share a name but are different deployments. Reading the wrong client’s address into a tx on the other chain is a foot-gun the SDK avoids by isolating each client’s frozen addresses object.

apyUSDRateView is Ethereum-only

eth.apyUSDRateView          // present
baseClient.apyUSDRateView   // undefined

Cross-chain code should always guard with ?.:

const apys = await Promise.all([
  eth.apyUSDRateView?.apy(),
  baseClient.apyUSDRateView?.apy(),
]);
// → [bigint, undefined]

CLI

The CLI is also per-session per-chain. Either:

  1. Switch profiles mid-session:

    $ apyx repl
    apyx> .env base
    apyx> await apyx.apyUSD.exchangeRate()
    
  2. Or fire two non-interactive subcommands:

    apyx apyUSD exchange-rate --chain ethereum --rpc-url $ETH
    apyx apyUSD exchange-rate --chain base     --rpc-url $BASE
    

A single REPL session can only have one active client at a time — running both chains side-by-side requires the SDK script approach above.

Sharing an account across chains

The account object can be reused — viem accounts are chain-agnostic.

import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(process.env.PK as `0x${string}`);

const eth = createApyxClient({ chain: mainnet, transport: http(ethRpc), account });
const baseClient = createApyxClient({ chain: base, transport: http(baseRpc), account });

await Promise.all([
  eth.apxUSD.balanceOf(account.address),
  baseClient.apxUSD.balanceOf(account.address),
]);

Same address, two different on-chain balances. Useful for treasury dashboards.

Verifying the recipe

The cross-chain pattern is exercised by getAddresses + factory tests under test/e2e/sdk/apxUSD.fork.spec.ts and test/e2e/sdk/apyUSD.fork.spec.ts (the fork specs run against an Ethereum mainnet fork; Base is verified via unit tests on the address registry plus playground smoke tests).

The browser playground also covers the user-facing path (chain switcher + per-chain RPC override) — see Playground Coverage.