Migration guide
Migrating to Prism is a URL change. The work is in removing the resilience code you no longer need - leaving it in place is what causes problems.
From a single provider
Replace the URL:
// Before
const client = createPublicClient({
chain: robinhoodChain,
transport: http('https://robinhood-mainnet.g.alchemy.com/v2/KEY'),
});
// After
const client = createPublicClient({
chain: robinhoodChain,
transport: http('https://mainnet.prismrpc.co/v1/YOUR_API_KEY'),
});Nothing else changes. Responses are byte-compatible with what you had.
From a hand-rolled fallback list
This is the important case. If you already rotate providers on failure, that logic must come out.
// Before - remove all of this
const PROVIDERS = [
'https://robinhood-mainnet.g.alchemy.com/v2/KEY',
'https://rpc.ankr.com/robinhood/KEY',
'https://public.robinhoodchain.com',
];
async function callWithFallback(payload, index = 0) {
try {
const response = await fetch(PROVIDERS[index], {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error(String(response.status));
return await response.json();
} catch (error) {
if (index < PROVIDERS.length - 1) return callWithFallback(payload, index + 1);
throw error;
}
}// After
async function call(payload) {
const response = await fetch(process.env.PRISM_RPC_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
return response.json();
}Keeping the old loop on top of Prism multiplies requests during an incident, and your loop cannot see breaker state - it will retry providers Prism has already excluded for good reason.
What to remove
| Remove | Because |
|---|---|
| Provider fallback lists | Prism pools them |
| Retry wrappers around reads | Reads are already retried across providers |
| Client-side health checks | Prism probes every provider continuously |
| Latency racing between providers | The router already picks the fastest |
| Manual timeouts under 10s | Too short to survive a failover |
What to keep
| Keep | Because |
|---|---|
| Transaction resubmission logic | Prism never replays a submission - that is still yours |
| Nonce management | Prism does not track nonces |
| Application-level caching | Cheaper than any network call |
| Alerting on your own p99 | Measures what users experience |
Migrating a transaction path
Submissions need care because Prism deliberately does not retry them.
import { keccak256 } from 'viem';
async function submit(signedTransaction) {
const hash = keccak256(signedTransaction);
try {
await client.request({
method: 'eth_sendRawTransaction',
params: [signedTransaction],
});
} catch (error) {
// Ambiguous, not failed. Check before doing anything else.
for (let attempt = 0; attempt < 10; attempt++) {
const receipt = await client.request({
method: 'eth_getTransactionReceipt',
params: [hash],
});
if (receipt) return receipt;
await new Promise((resolve) => setTimeout(resolve, 2000));
}
throw error;
}
return waitForReceipt(hash);
}A staged rollout
- Testnet first. Point staging at a testnet key for a few days. Confirm your method mix is fully supported on the methods page.
- Split reads. Move read traffic to Prism while transaction submission stays on your existing provider. Reads are the safe majority.
- Watch the headers. Log
X-Prism-ProviderandX-Prism-Attempts. A low failover rate means the pool is healthy under your traffic. - Move writes. Migrate submission last, once the timeout-handling path above is in place.
- Delete the old code. Remove the fallback list and retry wrappers. This step is what actually delivers the reliability improvement.
Rolling back
Point the URL at your previous provider. There is no state to migrate, no data held on your behalf, and no format to convert - which is the point of staying protocol-compatible.
