Skip to content

Commit cd151d9

Browse files
authored
Custom_Chain (#63)
1 parent 797a8bd commit cd151d9

File tree

11 files changed

+142
-16
lines changed

11 files changed

+142
-16
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# Changelog
2+
## [5.1.2] - 2025-05-07
3+
### Fix
4+
- Added ability to use custom chain
5+
26
## [5.1.1] - 2025-03-19
37
### Fix
48
- Removed multiple estimations when paymaster is set in the userOp

examples/basics/custom-chain.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { printOp } from '../../src/sdk/common/OperationUtils';
2+
import * as dotenv from 'dotenv';
3+
import { sleep } from '../../src/sdk/common';
4+
import { defineChain, parseEther } from 'viem';
5+
import { EtherspotBundler, ModularSdk } from '../../src';
6+
7+
dotenv.config();
8+
9+
const recipient = '0x80a1874E1046B1cc5deFdf4D3153838B72fF94Ac'; // recipient wallet address
10+
const value = '0.0000001'; // transfer value
11+
const bundlerApiKey = 'etherspot_public_key';
12+
const bundlerUrl = 'https://testnet-rpc.etherspot.io/v2/84532'; // bundler url
13+
const chainId = 84532; // chain id
14+
const entryPointAddress = '0x0000000071727De22E5E9d8BAf0edAc6f37da032'; // entry point address
15+
const walletFactoryAddress = '0x2A40091f044e48DEB5C0FCbc442E443F3341B451'; // wallet factory address
16+
const bootstrapAddress = '0x0D5154d7751b6e2fDaa06F0cC9B400549394C8AA'; // bootstrap address
17+
const multipleOwnerECDSAValidatorAddress = '0x0740Ed7c11b9da33d9C80Bd76b826e4E90CC1906'; // multi owner ECDSA validator factory address
18+
19+
// tsx examples/basics/custom-chain.ts
20+
async function main() {
21+
// for custom chains, you can use the following code to create a chain object
22+
const chain = defineChain({
23+
id: chainId,
24+
name: "Base sepolia Testnet",
25+
nativeCurrency: {
26+
decimals: 18,
27+
name: 'ETH',
28+
symbol: 'ETH'
29+
},
30+
rpcUrls: {
31+
default: {
32+
http: ['https://sepolia.base.org'] // RPC URL
33+
}
34+
}
35+
})
36+
// initializating sdk...
37+
const modularSdk = new ModularSdk(
38+
process.env.WALLET_PRIVATE_KEY as string,
39+
{
40+
chain: chain,
41+
chainId: chainId,
42+
bundlerProvider: new EtherspotBundler(chainId, bundlerApiKey, bundlerUrl),
43+
index: 0,
44+
entryPointAddress,
45+
walletFactoryAddress,
46+
bootstrapAddress,
47+
multipleOwnerECDSAValidatorAddress,
48+
rpcProviderUrl: bundlerUrl,
49+
})
50+
51+
52+
// get address of EtherspotWallet...
53+
const address: string = await modularSdk.getCounterFactualAddress();
54+
console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`);
55+
56+
// clear the transaction batch
57+
await modularSdk.clearUserOpsFromBatch();
58+
59+
// add transactions to the batch
60+
const transactionBatch = await modularSdk.addUserOpsToBatch({ to: recipient, value: parseEther(value) });
61+
console.log('transactions: ', transactionBatch);
62+
63+
// get balance of the account address
64+
const balance = await modularSdk.getNativeBalance();
65+
66+
console.log('balances: ', balance);
67+
68+
// estimate transactions added to the batch and get the fee data for the UserOp
69+
const op = await modularSdk.estimate();
70+
console.log(`Estimate UserOp: ${await printOp(op)}`);
71+
72+
// sign the UserOp and sending to the bundler...
73+
const uoHash = await modularSdk.send(op);
74+
console.log(`UserOpHash: ${uoHash}`);
75+
76+
// get transaction hash...
77+
console.log('Waiting for transaction...');
78+
let userOpsReceipt: string | null = null;
79+
const timeout = Date.now() + 1200000; // 1 minute timeout
80+
while ((userOpsReceipt == null) && (Date.now() < timeout)) {
81+
await sleep(2);
82+
userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash);
83+
}
84+
console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt);
85+
}
86+
87+
main()
88+
.catch(console.error)
89+
.finally(() => process.exit());

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@etherspot/modular-sdk",
3-
"version": "5.1.1",
3+
"version": "5.1.2",
44
"description": "Etherspot Modular SDK - build with ERC-7579 smart accounts modules",
55
"keywords": [
66
"ether",

src/sdk/base/BaseAccountAPI.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export abstract class BaseAccountAPI {
6868
const optionsLike = params.optionsLike;
6969

7070
const {
71+
chain,
7172
chainId, //
7273
rpcProviderUrl,
7374
factoryWallet,
@@ -78,7 +79,7 @@ export abstract class BaseAccountAPI {
7879
networkService: new NetworkService(chainId),
7980
walletService: new WalletService(
8081
params.wallet,
81-
{ provider: rpcProviderUrl},
82+
{ provider: rpcProviderUrl, chain},
8283
bundlerProvider.url,
8384
chainId
8485
),
@@ -94,7 +95,7 @@ export abstract class BaseAccountAPI {
9495
this.accountAddress = params.accountAddress;
9596
this.factoryAddress = params.factoryAddress;
9697
this.publicClient = params.publicClient;
97-
this.validatorAddress = Networks[params.optionsLike.chainId]?.contracts?.multipleOwnerECDSAValidator ?? DEFAULT_MULTIPLE_OWNER_ECDSA_VALIDATOR_ADDRESS;
98+
this.validatorAddress = params.optionsLike?.multipleOwnerECDSAValidatorAddress ?? Networks[params.optionsLike.chainId]?.contracts?.multipleOwnerECDSAValidator ?? DEFAULT_MULTIPLE_OWNER_ECDSA_VALIDATOR_ADDRESS;
9899
}
99100

100101
get error$(): ErrorSubject {

src/sdk/base/EtherspotWalletAPI.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class EtherspotWalletAPI extends BaseAccountAPI {
5454
this.index = params.index ?? 0;
5555
this.predefinedAccountAddress = params.predefinedAccountAddress ?? null;
5656
if (params?.optionsLike) {
57-
this.bootstrapAddress = Networks[params.optionsLike.chainId]?.contracts?.bootstrap ?? DEFAULT_BOOTSTRAP_ADDRESS;
57+
this.bootstrapAddress = params.optionsLike?.bootstrapAddress ?? Networks[params.optionsLike.chainId]?.contracts?.bootstrap ?? DEFAULT_BOOTSTRAP_ADDRESS;
5858
} else {
5959
this.bootstrapAddress = DEFAULT_BOOTSTRAP_ADDRESS;
6060
}

src/sdk/common/utils/viem-utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ export const getPublicClient = ({ chainId, transport }: { chainId: number, trans
2626
return publicClient;
2727
}
2828

29+
export const getPublicClientByChain = ({ chain, transport }: { chain: Chain, transport: Transport }) => {
30+
const publicClient = createPublicClient({
31+
chain: chain,
32+
transport: transport
33+
});
34+
return publicClient;
35+
}
36+
2937
export const getWalletClientFromPrivateKey = ({ rpcUrl, chainId, privateKey }: { rpcUrl: string, chainId: number, privateKey: string }): ReturnType<typeof createWalletClient> => {
3038
return createWalletClient({
3139
account: privateKeyToAccount(privateKey as Hex),

src/sdk/interfaces.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Chain } from 'viem';
12
import { BundlerProviderLike } from './bundler/index.js';
23

34
export interface PaymasterApi {
@@ -11,11 +12,14 @@ export enum Factory {
1112

1213
export interface SdkOptions {
1314
chainId: number;
15+
chain?: Chain;
1416
bundlerProvider?: BundlerProviderLike;
1517
rpcProviderUrl?: string;
1618
factoryWallet?: Factory;
1719
walletFactoryAddress?: string;
1820
entryPointAddress?: string;
1921
accountAddress?: string;
2022
index?: number;
23+
bootstrapAddress?: string;
24+
multipleOwnerECDSAValidatorAddress?: string;
2125
}

src/sdk/sdk.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { Network } from "./network/index.js";
33
import {
44
BatchUserOpsRequest, Exception, getGasFee,
55
getViemAddress, MODULE_TYPE,
6-
UserOperation, UserOpsRequest, getPublicClient
6+
UserOperation, UserOpsRequest, getPublicClient,
7+
getPublicClientByChain
78
} from "./common/index.js";
89
import {
910
EthereumProvider,
@@ -52,6 +53,7 @@ export class ModularSdk {
5253
chainId,
5354
rpcProviderUrl,
5455
accountAddress,
56+
chain,
5557
} = optionsLike;
5658

5759
this.chainId = chainId;
@@ -72,12 +74,24 @@ export class ModularSdk {
7274

7375
this.providerUrl = viemClientUrl;
7476

75-
this.publicClient = getPublicClient({
76-
chainId: chainId,
77-
transport: http(
78-
viemClientUrl
79-
)
80-
}) as PublicClient;
77+
if (Networks[chainId] == undefined) {
78+
if (chain == undefined) {
79+
throw new Exception('chain needs to be set when chainId is not in default Networks');
80+
}
81+
this.publicClient = getPublicClientByChain({
82+
chain: chain,
83+
transport: http(
84+
viemClientUrl
85+
)
86+
}) as PublicClient;
87+
} else {
88+
this.publicClient = getPublicClient({
89+
chainId: chainId,
90+
transport: http(
91+
viemClientUrl
92+
)
93+
}) as PublicClient;
94+
}
8195

8296
let entryPointAddress = '', walletFactoryAddress = '';
8397
if (Networks[chainId]) {

src/sdk/wallet/interfaces.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import { Chain } from "viem";
2+
13
export interface Wallet {
24
address: string;
35
providerType: string;
46
}
57

68
export interface WalletOptions {
79
provider?: string;
10+
chain?: Chain;
811
}

src/sdk/wallet/providers/key.wallet-provider.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Hash, Hex, TransactionRequest, WalletClient, createWalletClient, http, concat, Address, encodeAbiParameters, parseAbiParameters, hashMessage, toBytes } from 'viem';
1+
import { Hash, Hex, TransactionRequest, WalletClient, createWalletClient, http, concat, Address, encodeAbiParameters, parseAbiParameters, hashMessage, toBytes, Chain } from 'viem';
22
import { MessagePayload, WalletProvider } from './interfaces.js';
33
import { privateKeyToAccount } from 'viem/accounts';
44
import { Networks } from '../../network/index.js';
@@ -10,10 +10,10 @@ export class KeyWalletProvider implements WalletProvider {
1010

1111
readonly wallet: WalletClient;
1212

13-
constructor(chainId: number, privateKey: string) {
13+
constructor(chainId: number, privateKey: string, chain?: Chain) {
1414
this.wallet = createWalletClient({
1515
account: privateKeyToAccount(privateKey as Hex),
16-
chain: Networks[chainId].chain,
16+
chain: Networks[chainId]?.chain ?? chain,
1717
transport: http()
1818
});
1919

src/sdk/wallet/wallet.service.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { map, Subscription } from 'rxjs';
22
import {
33
Address,
4+
Chain,
45
Hash,
56
Hex,
67
TransactionRequest,
@@ -14,6 +15,7 @@ export class WalletService extends Service {
1415
readonly wallet$ = new ObjectSubject<Wallet>();
1516
readonly rpcBundlerUrl: string;
1617
readonly chainId: number;
18+
readonly chainObject: Chain | undefined;
1719

1820
provider: WalletProvider;
1921

@@ -26,6 +28,7 @@ export class WalletService extends Service {
2628
super();
2729
this.rpcBundlerUrl = rpcUrl;
2830
this.chainId = chain;
31+
this.chainObject = options.chain;
2932
}
3033

3134
get wallet(): Wallet {
@@ -77,15 +80,15 @@ export class WalletService extends Service {
7780
const walletLike = providerLike as WalletClient;
7881
const isNotViemClient = walletLike?.account.address === undefined;
7982
if (privateKey && isNotViemClient) {
80-
provider = new KeyWalletProvider(this.chainId, privateKey);
83+
provider = new KeyWalletProvider(this.chainId, privateKey, this.chainObject);
8184
} else {
8285
provider = new WalletClientProvider(walletLike);
8386
}
8487
break;
8588
}
8689

8790
case 'string':
88-
provider = new KeyWalletProvider(this.chainId, providerLike);
91+
provider = new KeyWalletProvider(this.chainId, providerLike, this.chainObject);
8992
break;
9093
}
9194
}

0 commit comments

Comments
 (0)