Skip to content

Commit 39beb1d

Browse files
authored
Add fallback RPC URLs for Base networks (#88)
* Add fallback RPC URLs for Base networks * use viem values * adjust signer construction
1 parent 18fe615 commit 39beb1d

File tree

3 files changed

+128
-12
lines changed

3 files changed

+128
-12
lines changed

packages/account-sdk/src/sign/base-account/Signer.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
import { parseErrorMessageFromAny } from ':core/telemetry/utils.js';
3232
import { Address } from ':core/type/index.js';
3333
import { ensureIntNumber, hexStringFromNumber } from ':core/type/util.js';
34-
import { SDKChain, createClients, getClient } from ':store/chain-clients/utils.js';
34+
import { FALLBACK_CHAINS, SDKChain, createClients, getClient } from ':store/chain-clients/utils.js';
3535
import { correlationIds } from ':store/correlation-ids/store.js';
3636
import { store } from ':store/store.js';
3737
import { assertArrayPresence, assertPresence } from ':util/assertPresence.js';
@@ -90,9 +90,8 @@ export class Signer {
9090
id: params.metadata.appChainIds?.[0] ?? 1,
9191
};
9292

93-
if (chains) {
94-
createClients(chains);
95-
}
93+
// Use fallback chains if no chains are provided
94+
createClients(chains ?? FALLBACK_CHAINS);
9695
}
9796

9897
public get isConnected() {

packages/account-sdk/src/store/chain-clients/utils.test.ts

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
import { optimismSepolia, sepolia } from 'viem/chains';
1+
import { base, baseSepolia, optimismSepolia, sepolia } from 'viem/chains';
22

33
import { ChainClients } from './store.js';
44
import { createClients } from './utils.js';
55

66
describe('chain-clients/utils', () => {
7+
beforeEach(() => {
8+
// Clear the ChainClients state before each test
9+
ChainClients.setState(() => ({}), true);
10+
});
11+
712
it('should create clients', () => {
813
createClients([
914
{
@@ -47,4 +52,81 @@ describe('chain-clients/utils', () => {
4752
expect(ChainClients.getState()[sepolia.id].bundlerClient).toBeDefined();
4853
expect(ChainClients.getState()[optimismSepolia.id].bundlerClient).toBeDefined();
4954
});
55+
56+
describe('fallback RPC URLs', () => {
57+
it('should use fallback RPC URL for Base mainnet when wallet does not provide one', () => {
58+
createClients([
59+
{
60+
id: base.id, // Base mainnet
61+
// No rpcUrl provided
62+
},
63+
]);
64+
65+
expect(ChainClients.getState()[base.id]).toBeDefined();
66+
expect(ChainClients.getState()[base.id].client).toBeDefined();
67+
expect(ChainClients.getState()[base.id].bundlerClient).toBeDefined();
68+
});
69+
70+
it('should use fallback RPC URL for Base Sepolia when wallet does not provide one', () => {
71+
createClients([
72+
{
73+
id: baseSepolia.id, // Base Sepolia
74+
// No rpcUrl provided
75+
},
76+
]);
77+
78+
expect(ChainClients.getState()[baseSepolia.id]).toBeDefined();
79+
expect(ChainClients.getState()[baseSepolia.id].client).toBeDefined();
80+
expect(ChainClients.getState()[baseSepolia.id].bundlerClient).toBeDefined();
81+
});
82+
83+
it('should prefer wallet-provided RPC URL over fallback for Base mainnet', () => {
84+
const customRpcUrl = 'https://custom.base.rpc.url';
85+
createClients([
86+
{
87+
id: base.id, // Base mainnet
88+
rpcUrl: customRpcUrl,
89+
},
90+
]);
91+
92+
expect(ChainClients.getState()[base.id]).toBeDefined();
93+
expect(ChainClients.getState()[base.id].client).toBeDefined();
94+
// We can't directly test the RPC URL used, but we can verify the client was created
95+
});
96+
97+
it('should not create client for unsupported chain without RPC URL', () => {
98+
createClients([
99+
{
100+
id: 999999, // Unsupported chain
101+
// No rpcUrl provided
102+
},
103+
]);
104+
105+
expect(ChainClients.getState()[999999]).toBeUndefined();
106+
});
107+
108+
it('should handle mixed chains with and without RPC URLs', () => {
109+
createClients([
110+
{
111+
id: base.id, // Base mainnet - will use fallback
112+
},
113+
{
114+
id: sepolia.id,
115+
rpcUrl: sepolia.rpcUrls.default.http[0],
116+
},
117+
{
118+
id: 999999, // Unsupported chain - will be skipped
119+
},
120+
{
121+
id: baseSepolia.id, // Base Sepolia - will use fallback
122+
},
123+
]);
124+
125+
expect(Object.keys(ChainClients.getState()).length).toBe(3);
126+
expect(ChainClients.getState()[base.id]).toBeDefined();
127+
expect(ChainClients.getState()[sepolia.id]).toBeDefined();
128+
expect(ChainClients.getState()[baseSepolia.id]).toBeDefined();
129+
expect(ChainClients.getState()[999999]).toBeUndefined();
130+
});
131+
});
50132
});

packages/account-sdk/src/store/chain-clients/utils.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,59 @@
11
import { createPublicClient, defineChain, http, PublicClient } from 'viem';
22
import { BundlerClient, createBundlerClient } from 'viem/account-abstraction';
3+
import { base, baseSepolia } from 'viem/chains';
34

4-
import { ChainClients } from './store.js';
55
import { RPCResponseNativeCurrency } from ':core/message/RPCResponse.js';
6+
import { ChainClients } from './store.js';
67

78
export type SDKChain = {
89
id: number;
910
rpcUrl?: string;
1011
nativeCurrency?: RPCResponseNativeCurrency;
1112
};
1213

14+
// Fallback chains using viem's chain definitions directly
15+
export const FALLBACK_CHAINS: SDKChain[] = [
16+
{
17+
id: base.id,
18+
rpcUrl: base.rpcUrls.default.http[0],
19+
nativeCurrency: {
20+
name: base.nativeCurrency.name,
21+
symbol: base.nativeCurrency.symbol,
22+
decimal: base.nativeCurrency.decimals,
23+
},
24+
},
25+
{
26+
id: baseSepolia.id,
27+
rpcUrl: baseSepolia.rpcUrls.default.http[0],
28+
nativeCurrency: {
29+
name: baseSepolia.nativeCurrency.name,
30+
symbol: baseSepolia.nativeCurrency.symbol,
31+
decimal: baseSepolia.nativeCurrency.decimals,
32+
},
33+
},
34+
];
35+
1336
export function createClients(chains: SDKChain[]) {
1437
chains.forEach((c) => {
15-
if (!c.rpcUrl) {
38+
// Use fallback RPC URL for Base networks if wallet hasn't provided one
39+
let rpcUrl = c.rpcUrl;
40+
if (!rpcUrl) {
41+
const fallbackChain = FALLBACK_CHAINS.find((fc) => fc.id === c.id);
42+
if (fallbackChain) {
43+
rpcUrl = fallbackChain.rpcUrl;
44+
}
45+
}
46+
47+
// Skip if still no RPC URL available
48+
if (!rpcUrl) {
1649
return;
1750
}
51+
1852
const viemchain = defineChain({
1953
id: c.id,
2054
rpcUrls: {
2155
default: {
22-
http: [c.rpcUrl],
56+
http: [rpcUrl],
2357
},
2458
},
2559
name: c.nativeCurrency?.name ?? '',
@@ -32,19 +66,20 @@ export function createClients(chains: SDKChain[]) {
3266

3367
const client = createPublicClient({
3468
chain: viemchain,
35-
transport: http(c.rpcUrl),
69+
transport: http(rpcUrl),
3670
});
3771
const bundlerClient = createBundlerClient({
3872
client,
39-
transport: http(c.rpcUrl),
73+
transport: http(rpcUrl),
4074
});
4175

42-
ChainClients.setState({
76+
ChainClients.setState((state) => ({
77+
...state,
4378
[c.id]: {
4479
client,
4580
bundlerClient,
4681
},
47-
});
82+
}));
4883
});
4984
}
5085

0 commit comments

Comments
 (0)