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
- Address book is per-chain
apyUSDRateViewis Ethereum-only- CLI
- Sharing an account across chains
- Verifying the recipe
- Related
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:
-
Switch profiles mid-session:
$ apyx repl apyx> .env base apyx> await apyx.apyUSD.exchangeRate() -
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.
Related
- Supported Chains — address book and per-chain availability.
- Custom RPC — how to feed two paid endpoints.
- Addresses — registry + override semantics.