@obolnetwork/obol-sdk 2.4.4 → 2.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/package.json +6 -5
  3. package/dist/cjs/src/incentiveHelpers.js +2 -4
  4. package/dist/cjs/src/incentives.js +26 -5
  5. package/dist/cjs/src/index.js +3 -1
  6. package/dist/cjs/src/services.js +3 -2
  7. package/dist/cjs/src/types.js +0 -9
  8. package/dist/cjs/src/utils.js +4 -4
  9. package/dist/cjs/src/verification/common.js +12 -9
  10. package/dist/cjs/src/verification/signature-validator.js +6 -5
  11. package/dist/cjs/test/incentives.test.js +4 -4
  12. package/dist/cjs/test/methods.test.js +11 -0
  13. package/dist/esm/package.json +6 -5
  14. package/dist/esm/src/incentiveHelpers.js +3 -5
  15. package/dist/esm/src/incentives.js +26 -5
  16. package/dist/esm/src/index.js +2 -1
  17. package/dist/esm/src/services.js +3 -2
  18. package/dist/esm/src/splitHelpers.js +1 -1
  19. package/dist/esm/src/types.js +0 -9
  20. package/dist/esm/src/utils.js +4 -4
  21. package/dist/esm/src/verification/common.js +12 -9
  22. package/dist/esm/src/verification/signature-validator.js +6 -5
  23. package/dist/esm/test/incentives.test.js +4 -4
  24. package/dist/esm/test/methods.test.js +11 -0
  25. package/dist/types/src/incentiveHelpers.d.ts +3 -4
  26. package/dist/types/src/incentives.d.ts +29 -13
  27. package/dist/types/src/index.d.ts +15 -4
  28. package/dist/types/src/services.d.ts +3 -2
  29. package/dist/types/src/splitHelpers.d.ts +7 -8
  30. package/dist/types/src/types.d.ts +21 -5
  31. package/dist/types/src/utils.d.ts +5 -5
  32. package/dist/types/src/verification/common.d.ts +2 -2
  33. package/dist/types/src/verification/signature-validator.d.ts +5 -2
  34. package/package.json +6 -5
  35. package/src/incentiveHelpers.ts +5 -15
  36. package/src/incentives.ts +37 -33
  37. package/src/index.ts +27 -23
  38. package/src/services.ts +4 -2
  39. package/src/splitHelpers.ts +9 -14
  40. package/src/types.ts +37 -17
  41. package/src/utils.ts +11 -8
  42. package/src/verification/common.ts +13 -1
  43. package/src/verification/signature-validator.ts +10 -3
package/src/incentives.ts CHANGED
@@ -1,15 +1,11 @@
1
- import type {
2
- JsonRpcApiProvider,
3
- JsonRpcProvider,
4
- JsonRpcSigner,
5
- Provider,
6
- Signer,
7
- } from 'ethers';
8
1
  import { isContractAvailable } from './utils';
9
2
  import {
10
- type Incentives as IncentivesType,
3
+ type ClaimableIncentives,
11
4
  type ETH_ADDRESS,
12
5
  FORK_NAMES,
6
+ type ProviderType,
7
+ type SignerType,
8
+ type ClaimIncentivesResponse,
13
9
  } from './types';
14
10
  import {
15
11
  claimIncentivesFromMerkleDistributor,
@@ -17,31 +13,29 @@ import {
17
13
  } from './incentiveHelpers';
18
14
  import { DEFAULT_BASE_VERSION } from './constants';
19
15
 
16
+ /**
17
+ * Incentives can be used for fetching and claiming Obol incentives.
18
+ * @class
19
+ * @internal Access it through Client.incentives.
20
+ * @example
21
+ * const obolClient = new Client(config);
22
+ * await obolClient.incentives.claimIncentives(address);
23
+ */
20
24
  export class Incentives {
21
- private readonly signer: Signer | JsonRpcSigner | undefined;
25
+ private readonly signer: SignerType | undefined;
22
26
  public readonly chainId: number;
23
27
  private readonly request: (
24
28
  endpoint: string,
25
29
  options?: RequestInit,
26
30
  ) => Promise<any>;
27
31
 
28
- public readonly provider:
29
- | Provider
30
- | JsonRpcProvider
31
- | JsonRpcApiProvider
32
- | undefined
33
- | null;
32
+ public readonly provider: ProviderType | undefined | null;
34
33
 
35
34
  constructor(
36
- signer: Signer | JsonRpcSigner | undefined,
35
+ signer: SignerType | undefined,
37
36
  chainId: number,
38
37
  request: (endpoint: string, options?: RequestInit) => Promise<any>,
39
- provider:
40
- | Provider
41
- | JsonRpcProvider
42
- | JsonRpcApiProvider
43
- | undefined
44
- | null,
38
+ provider: ProviderType | undefined | null,
45
39
  ) {
46
40
  this.signer = signer;
47
41
  this.chainId = chainId;
@@ -50,20 +44,25 @@ export class Incentives {
50
44
  }
51
45
 
52
46
  /**
53
- * Claims obol incentives from a Merkle Distributor contract using just an address.
54
- * The method automatically fetches incentives data and checks if already claimed.
47
+ * Claims Obol incentives from a Merkle Distributor contract using an address.
48
+ *
49
+ * This method automatically fetches incentive data and verifies whether the incentives have already been claimed.
50
+ * If `txHash` is `null`, it indicates that the incentives were already claimed.
51
+ *
52
+ * Note: This method is not yet enabled and will throw an error if called.
55
53
  *
56
54
  * @remarks
57
55
  * **⚠️ Important:** If you're storing the private key in an `.env` file, ensure it is securely managed
58
56
  * and not pushed to version control.
59
57
  *
60
58
  * @param {string} address - The address to claim incentives for
61
- * @returns {Promise<{ txHash: string } | { alreadyClaimed: true }>} The transaction hash or already claimed status
59
+ * @returns {Promise<ClaimIncentivesResponse>} The transaction hash or already claimed status
62
60
  * @throws Will throw an error if the incentives data is not found or the claim fails
61
+ *
62
+ * An example of how to use claimIncentives:
63
+ * [obolClient](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L281)
63
64
  */
64
- async claimIncentives(
65
- address: string,
66
- ): Promise<{ txHash: string } | { alreadyClaimed: true }> {
65
+ async claimIncentives(address: string): Promise<ClaimIncentivesResponse> {
67
66
  if (!this.signer) {
68
67
  throw new Error('Signer is required in claimIncentives');
69
68
  }
@@ -77,7 +76,7 @@ export class Incentives {
77
76
 
78
77
  const isContractDeployed = await isContractAvailable(
79
78
  incentivesData.contract_address,
80
- this.provider as Provider,
79
+ this.provider as ProviderType,
81
80
  );
82
81
 
83
82
  if (!isContractDeployed) {
@@ -92,7 +91,7 @@ export class Incentives {
92
91
  );
93
92
 
94
93
  if (claimed) {
95
- return { alreadyClaimed: true };
94
+ return { txHash: null };
96
95
  }
97
96
 
98
97
  const { txHash } = await claimIncentivesFromMerkleDistributor({
@@ -118,13 +117,15 @@ export class Incentives {
118
117
  * @param {ETH_ADDRESS} index - operator index in merkle tree
119
118
  * @returns {Promise<boolean>} true if incentives are already claime
120
119
  *
120
+ *
121
+ * An example of how to use isClaimed:
122
+ * [obolClient](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L266)
121
123
  */
122
124
  async isClaimed(
123
125
  contractAddress: ETH_ADDRESS,
124
126
  index: number,
125
127
  ): Promise<boolean> {
126
128
  return await isClaimedFromMerkleDistributor(
127
- this.chainId,
128
129
  contractAddress,
129
130
  index,
130
131
  this.provider,
@@ -135,10 +136,13 @@ export class Incentives {
135
136
  * @param address - Operator address
136
137
  * @returns {Promise<IncentivesType>} The matched incentives from DB
137
138
  * @throws On not found if address not found.
139
+ *
140
+ * An example of how to use getIncentivesByAddress:
141
+ * [obolClient](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L250)
138
142
  */
139
- async getIncentivesByAddress(address: string): Promise<IncentivesType> {
143
+ async getIncentivesByAddress(address: string): Promise<ClaimableIncentives> {
140
144
  const network = FORK_NAMES[this.chainId];
141
- const incentives: IncentivesType = await this.request(
145
+ const incentives: ClaimableIncentives = await this.request(
142
146
  `/${DEFAULT_BASE_VERSION}/address/incentives/${network}/${address}`,
143
147
  {
144
148
  method: 'GET',
package/src/index.ts CHANGED
@@ -1,11 +1,4 @@
1
- import {
2
- ZeroAddress,
3
- type Provider,
4
- type Signer,
5
- type JsonRpcSigner,
6
- type JsonRpcProvider,
7
- type JsonRpcApiProvider,
8
- } from 'ethers';
1
+ import { ZeroAddress } from 'ethers';
9
2
  import { v4 as uuidv4 } from 'uuid';
10
3
  import { Base } from './base.js';
11
4
  import {
@@ -37,6 +30,8 @@ import {
37
30
  type ClusterValidator,
38
31
  type ETH_ADDRESS,
39
32
  type OWRTranches,
33
+ type ProviderType,
34
+ type SignerType,
40
35
  } from './types.js';
41
36
  import { clusterConfigOrDefinitionHash } from './verification/common.js';
42
37
  import { validatePayload } from './ajv.js';
@@ -59,19 +54,28 @@ export * from './types.js';
59
54
  export * from './services.js';
60
55
  export * from './verification/signature-validator.js';
61
56
  export * from './verification/common.js';
57
+ export { Incentives } from './incentives.js';
62
58
 
63
59
  /**
64
60
  * Obol sdk Client can be used for creating, managing and activating distributed validators.
65
61
  */
66
62
  export class Client extends Base {
67
- private readonly signer: Signer | JsonRpcSigner | undefined;
63
+ /**
64
+ * The signer used for signing transactions.
65
+ */
66
+ private readonly signer: SignerType | undefined;
67
+
68
+ /**
69
+ * The incentives module, responsible for managing Obol tokens distribution.
70
+ * @type {Incentives}
71
+ */
68
72
  public incentives: Incentives;
69
- public provider:
70
- | Provider
71
- | JsonRpcProvider
72
- | JsonRpcApiProvider
73
- | undefined
74
- | null;
73
+
74
+ /**
75
+ * The blockchain provider, used to interact with the network.
76
+ * It can be null, undefined, or a valid provider instance and defaults to the Signer provider if Signer is passed.
77
+ */
78
+ public provider: ProviderType | undefined | null;
75
79
 
76
80
  /**
77
81
  * @param config - Client configurations
@@ -85,8 +89,8 @@ export class Client extends Base {
85
89
  */
86
90
  constructor(
87
91
  config: { baseUrl?: string; chainId?: number },
88
- signer?: Signer | JsonRpcSigner,
89
- provider?: Provider | JsonRpcProvider,
92
+ signer?: SignerType,
93
+ provider?: ProviderType,
90
94
  ) {
91
95
  super(config);
92
96
  this.signer = signer;
@@ -201,19 +205,19 @@ export class Client extends Base {
201
205
 
202
206
  const checkSplitMainAddress = await isContractAvailable(
203
207
  CHAIN_CONFIGURATION[this.chainId].SPLITMAIN_ADDRESS.address,
204
- this.signer.provider as Provider,
208
+ this.signer.provider as ProviderType,
205
209
  CHAIN_CONFIGURATION[this.chainId].SPLITMAIN_ADDRESS.bytecode,
206
210
  );
207
211
 
208
212
  const checkMulticallAddress = await isContractAvailable(
209
213
  CHAIN_CONFIGURATION[this.chainId].MULTICALL_ADDRESS.address,
210
- this.signer.provider as Provider,
214
+ this.signer.provider as ProviderType,
211
215
  CHAIN_CONFIGURATION[this.chainId].MULTICALL_ADDRESS.bytecode,
212
216
  );
213
217
 
214
218
  const checkOWRFactoryAddress = await isContractAvailable(
215
219
  CHAIN_CONFIGURATION[this.chainId].OWR_FACTORY_ADDRESS.address,
216
- this.signer.provider as Provider,
220
+ this.signer.provider as ProviderType,
217
221
  CHAIN_CONFIGURATION[this.chainId].OWR_FACTORY_ADDRESS.bytecode,
218
222
  );
219
223
 
@@ -251,7 +255,7 @@ export class Client extends Base {
251
255
 
252
256
  const isSplitterDeployed = await isContractAvailable(
253
257
  predictedSplitterAddress,
254
- this.signer.provider as Provider,
258
+ this.signer.provider as ProviderType,
255
259
  );
256
260
 
257
261
  const { withdrawal_address, fee_recipient_address } =
@@ -316,7 +320,7 @@ export class Client extends Base {
316
320
 
317
321
  const checkSplitMainAddress = await isContractAvailable(
318
322
  CHAIN_CONFIGURATION[this.chainId].SPLITMAIN_ADDRESS.address,
319
- this.signer.provider as Provider,
323
+ this.signer.provider as ProviderType,
320
324
  CHAIN_CONFIGURATION[this.chainId].SPLITMAIN_ADDRESS.bytecode,
321
325
  );
322
326
 
@@ -349,7 +353,7 @@ export class Client extends Base {
349
353
 
350
354
  const isSplitterDeployed = await isContractAvailable(
351
355
  predictedSplitterAddress,
352
- this.signer.provider as Provider,
356
+ this.signer.provider as ProviderType,
353
357
  );
354
358
 
355
359
  if (!isSplitterDeployed) {
package/src/services.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { type ClusterLock } from './types.js';
1
+ import { type SafeRpcUrl, type ClusterLock } from './types.js';
2
2
  import { isValidClusterLock } from './verification/common.js';
3
3
 
4
4
  /**
5
5
  * Verifies Cluster Lock's validity.
6
6
  * @param lock - cluster lock
7
+ * @param safeRpcUrl - optional safeRpcUrl for safe wallet verification
7
8
  * @returns {Promise<{ result: boolean }> } boolean result to indicate if lock is valid
8
9
  * @throws on missing keys or values.
9
10
  *
@@ -12,9 +13,10 @@ import { isValidClusterLock } from './verification/common.js';
12
13
  */
13
14
  export const validateClusterLock = async (
14
15
  lock: ClusterLock,
16
+ safeRpcUrl?: SafeRpcUrl,
15
17
  ): Promise<boolean> => {
16
18
  try {
17
- const isLockValid = await isValidClusterLock(lock);
19
+ const isLockValid = await isValidClusterLock(lock, safeRpcUrl);
18
20
  return isLockValid;
19
21
  } catch (err: any) {
20
22
  throw err;
@@ -3,14 +3,9 @@ import {
3
3
  type ClusterValidator,
4
4
  type ETH_ADDRESS,
5
5
  type SplitRecipient,
6
+ type SignerType,
6
7
  } from './types';
7
- import {
8
- Contract,
9
- Interface,
10
- parseEther,
11
- ZeroAddress,
12
- type Signer,
13
- } from 'ethers';
8
+ import { Contract, Interface, parseEther, ZeroAddress } from 'ethers';
14
9
  import { OWRContract, OWRFactoryContract } from './abi/OWR';
15
10
  import { splitMainEthereumAbi } from './abi/SplitMain';
16
11
  import { MultiCallContract } from './abi/Multicall';
@@ -59,7 +54,7 @@ export const predictSplitterAddress = async ({
59
54
  distributorFee,
60
55
  controllerAddress,
61
56
  }: {
62
- signer: Signer;
57
+ signer: SignerType;
63
58
  accounts: ETH_ADDRESS[];
64
59
  percentAllocations: number[];
65
60
  chainId: number;
@@ -111,7 +106,7 @@ export const handleDeployOWRAndSplitter = async ({
111
106
  controllerAddress,
112
107
  recoveryAddress,
113
108
  }: {
114
- signer: Signer;
109
+ signer: SignerType;
115
110
  isSplitterDeployed: boolean;
116
111
  predictedSplitterAddress: ETH_ADDRESS;
117
112
  accounts: ETH_ADDRESS[];
@@ -174,7 +169,7 @@ const createOWRContract = async ({
174
169
  chainId,
175
170
  }: {
176
171
  owrArgs: OWRArgs;
177
- signer: Signer;
172
+ signer: SignerType;
178
173
  chainId: number;
179
174
  }): Promise<ETH_ADDRESS> => {
180
175
  try {
@@ -209,7 +204,7 @@ export const deploySplitterContract = async ({
209
204
  distributorFee,
210
205
  controllerAddress,
211
206
  }: {
212
- signer: Signer;
207
+ signer: SignerType;
213
208
  accounts: ETH_ADDRESS[];
214
209
  percentAllocations: number[];
215
210
  chainId: number;
@@ -247,7 +242,7 @@ export const deploySplitterAndOWRContracts = async ({
247
242
  }: {
248
243
  owrArgs: OWRArgs;
249
244
  splitterArgs: SplitArgs;
250
- signer: Signer;
245
+ signer: SignerType;
251
246
  chainId: number;
252
247
  }): Promise<{ owrAddress: ETH_ADDRESS; splitterAddress: ETH_ADDRESS }> => {
253
248
  const executeCalls: Call[] = [];
@@ -304,7 +299,7 @@ export const getOWRTranches = async ({
304
299
  signer,
305
300
  }: {
306
301
  owrAddress: ETH_ADDRESS;
307
- signer: Signer;
302
+ signer: SignerType;
308
303
  }): Promise<OWRTranches> => {
309
304
  const owrContract = new Contract(owrAddress, OWRContract.abi, signer);
310
305
  const res = await owrContract.getTranches();
@@ -318,7 +313,7 @@ export const getOWRTranches = async ({
318
313
 
319
314
  export const multicall = async (
320
315
  calls: Call[],
321
- signer: Signer,
316
+ signer: SignerType,
322
317
  multicallAddress: string,
323
318
  ): Promise<any> => {
324
319
  const multiCallContractInstance = new Contract(
package/src/types.ts CHANGED
@@ -1,3 +1,13 @@
1
+ import {
2
+ type Wallet,
3
+ type ethers,
4
+ type JsonRpcApiProvider,
5
+ type JsonRpcProvider,
6
+ type JsonRpcSigner,
7
+ type Provider,
8
+ type Signer,
9
+ } from 'ethers';
10
+
1
11
  /**
2
12
  * Permitted ChainID's
3
13
  */
@@ -21,28 +31,14 @@ export enum FORK_MAPPING {
21
31
  '0x10000910' = 560048,
22
32
  }
23
33
 
24
- /**
25
- * Permitted Chain Names
26
- */
27
34
  export const FORK_NAMES: Record<number, string> = {
28
- /** Mainnet. */
29
35
  [FORK_MAPPING['0x00000000']]: 'mainnet',
30
-
31
- /** Goerli/Prater. */
32
36
  [FORK_MAPPING['0x00001020']]: 'goerli',
33
-
34
- /** Gnosis Chain. */
35
37
  [FORK_MAPPING['0x00000064']]: 'gnosis',
36
-
37
- /** Holesky. */
38
38
  [FORK_MAPPING['0x01017000']]: 'holesky',
39
-
40
- /** Sepolia. */
41
39
  [FORK_MAPPING['0x90000069']]: 'sepolia',
42
-
43
- /** Hoodi. */
44
40
  [FORK_MAPPING['0x10000910']]: 'hoodi',
45
- };
41
+ } as const;
46
42
 
47
43
  /**
48
44
  * Node operator data
@@ -300,9 +296,9 @@ export type ClusterLock = {
300
296
  };
301
297
 
302
298
  /**
303
- * Incentives
299
+ * Claimable Obol Incentives
304
300
  */
305
- export type Incentives = {
301
+ export type ClaimableIncentives = {
306
302
  /** Operator Address. */
307
303
  operator_address: string;
308
304
 
@@ -323,3 +319,27 @@ export type Incentives = {
323
319
  * String expected to be Ethereum Address
324
320
  */
325
321
  export type ETH_ADDRESS = string;
322
+
323
+ /**
324
+ * Provider Types
325
+ */
326
+ export type ProviderType =
327
+ | Provider
328
+ | JsonRpcProvider
329
+ | JsonRpcApiProvider
330
+ | ethers.BrowserProvider;
331
+
332
+ /**
333
+ * Safe Wallet Provider Types
334
+ */
335
+ export type SafeRpcUrl = string;
336
+
337
+ /**
338
+ * Signer Types
339
+ */
340
+ export type SignerType = Signer | JsonRpcSigner | Wallet;
341
+
342
+ /**
343
+ * claimIncentives Response
344
+ */
345
+ export type ClaimIncentivesResponse = { txHash: string | null };
package/src/utils.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { ethers, type Provider } from 'ethers';
1
+ import { ethers } from 'ethers';
2
2
  import { DefinitionFlow, PROVIDER_MAP } from './constants';
3
- import { FORK_NAMES, type ClusterDefinition } from './types';
3
+ import { FORK_NAMES, type ProviderType, type ClusterDefinition } from './types';
4
4
 
5
5
  export const hexWithout0x = (hex: string): string => {
6
6
  return hex.slice(2, hex.length);
@@ -58,14 +58,14 @@ export const definitionFlow = (
58
58
 
59
59
  export const findDeployedBytecode = async (
60
60
  contractAddress: string,
61
- provider: Provider,
61
+ provider: ProviderType,
62
62
  ): Promise<string> => {
63
63
  return await provider?.getCode(contractAddress);
64
64
  };
65
65
 
66
66
  export const isContractAvailable = async (
67
67
  contractAddress: string,
68
- provider: Provider,
68
+ provider: ProviderType,
69
69
  bytecode?: string,
70
70
  ): Promise<boolean> => {
71
71
  const code = await findDeployedBytecode(contractAddress, provider);
@@ -76,10 +76,13 @@ export const isContractAvailable = async (
76
76
  return !!code && code !== '0x' && code !== '0x0';
77
77
  };
78
78
 
79
- export const getProvider = (chainId: number): ethers.Provider => {
80
- const rpcUrl = PROVIDER_MAP[chainId];
81
- if (!rpcUrl || rpcUrl === 'undefined') {
79
+ export const getProvider = (
80
+ chainId: number,
81
+ rpcUrl?: string,
82
+ ): ethers.JsonRpcProvider => {
83
+ const resolvedRpcUrl = rpcUrl ?? PROVIDER_MAP[chainId];
84
+ if (chainId && (!resolvedRpcUrl || resolvedRpcUrl === 'undefined')) {
82
85
  throw new Error(`No provider configured for ${FORK_NAMES[chainId]}`);
83
86
  }
84
- return new ethers.JsonRpcProvider(rpcUrl);
87
+ return new ethers.JsonRpcProvider(resolvedRpcUrl);
85
88
  };
@@ -1,7 +1,6 @@
1
1
  import { fromHexString } from '@chainsafe/ssz';
2
2
  import elliptic from 'elliptic';
3
3
  import { init } from '@chainsafe/bls';
4
-
5
4
  import {
6
5
  FORK_MAPPING,
7
6
  type ClusterDefinition,
@@ -9,6 +8,7 @@ import {
9
8
  type DepositData,
10
9
  type BuilderRegistrationMessage,
11
10
  type DistributedValidator,
11
+ type SafeRpcUrl,
12
12
  } from '../types.js';
13
13
  import * as semver from 'semver';
14
14
  import {
@@ -167,6 +167,7 @@ const validatePOSTConfigHashSigner = async (
167
167
  signature: string,
168
168
  configHash: string,
169
169
  chainId: FORK_MAPPING,
170
+ safeRpcUrl?: SafeRpcUrl,
170
171
  ): Promise<boolean> => {
171
172
  try {
172
173
  const data = signCreatorConfigHashPayload(
@@ -179,6 +180,7 @@ const validatePOSTConfigHashSigner = async (
179
180
  token: signature,
180
181
  data,
181
182
  chainId,
183
+ safeRpcUrl,
182
184
  });
183
185
  } catch (err) {
184
186
  throw err;
@@ -190,6 +192,7 @@ const validatePUTConfigHashSigner = async (
190
192
  signature: string,
191
193
  configHash: string,
192
194
  chainId: number,
195
+ safeRpcUrl?: SafeRpcUrl,
193
196
  ): Promise<boolean> => {
194
197
  try {
195
198
  const data = signOperatorConfigHashPayload(
@@ -201,6 +204,7 @@ const validatePUTConfigHashSigner = async (
201
204
  token: signature,
202
205
  data,
203
206
  chainId,
207
+ safeRpcUrl,
204
208
  });
205
209
  } catch (err) {
206
210
  throw err;
@@ -212,6 +216,7 @@ const validateEnrSigner = async (
212
216
  signature: string,
213
217
  payload: string,
214
218
  chainId: number,
219
+ safeRpcUrl?: SafeRpcUrl,
215
220
  ): Promise<boolean> => {
216
221
  try {
217
222
  const data = signEnrPayload({ enr: payload }, chainId);
@@ -221,6 +226,7 @@ const validateEnrSigner = async (
221
226
  token: signature,
222
227
  data,
223
228
  chainId,
229
+ safeRpcUrl,
224
230
  });
225
231
  } catch (err) {
226
232
  throw err;
@@ -230,6 +236,7 @@ const validateEnrSigner = async (
230
236
  const verifyDefinitionSignatures = async (
231
237
  clusterDefinition: ClusterDefinition,
232
238
  definitionType: DefinitionFlow,
239
+ safeRpcUrl?: SafeRpcUrl,
233
240
  ): Promise<boolean> => {
234
241
  if (definitionType === DefinitionFlow.Charon) {
235
242
  return true;
@@ -239,6 +246,7 @@ const verifyDefinitionSignatures = async (
239
246
  clusterDefinition.creator.config_signature as string,
240
247
  clusterDefinition.config_hash,
241
248
  FORK_MAPPING[clusterDefinition.fork_version as keyof typeof FORK_MAPPING],
249
+ safeRpcUrl,
242
250
  );
243
251
 
244
252
  if (!isPOSTConfigHashSignerValid) {
@@ -256,6 +264,7 @@ const verifyDefinitionSignatures = async (
256
264
  FORK_MAPPING[
257
265
  clusterDefinition.fork_version as keyof typeof FORK_MAPPING
258
266
  ],
267
+ safeRpcUrl,
259
268
  );
260
269
 
261
270
  const isENRSignerValid = await validateEnrSigner(
@@ -265,6 +274,7 @@ const verifyDefinitionSignatures = async (
265
274
  FORK_MAPPING[
266
275
  clusterDefinition.fork_version as keyof typeof FORK_MAPPING
267
276
  ],
277
+ safeRpcUrl,
268
278
  );
269
279
 
270
280
  if (!isPUTConfigHashSignerValid || !isENRSignerValid) {
@@ -485,6 +495,7 @@ const verifyLockData = async (clusterLock: ClusterLock): Promise<boolean> => {
485
495
 
486
496
  export const isValidClusterLock = async (
487
497
  clusterLock: ClusterLock,
498
+ safeRpcUrl?: SafeRpcUrl,
488
499
  ): Promise<boolean> => {
489
500
  try {
490
501
  const definitionType = definitionFlow(clusterLock.cluster_definition);
@@ -494,6 +505,7 @@ export const isValidClusterLock = async (
494
505
  const isValidDefinitionData = await verifyDefinitionSignatures(
495
506
  clusterLock.cluster_definition,
496
507
  definitionType,
508
+ safeRpcUrl,
497
509
  );
498
510
  if (!isValidDefinitionData) {
499
511
  return false;
@@ -10,20 +10,23 @@ import { PROVIDER_MAP } from '../constants';
10
10
  import { hashTypedData } from '@safe-global/protocol-kit/dist/src/utils';
11
11
  import { type EIP712TypedData } from '@safe-global/safe-core-sdk-types';
12
12
  import { isContractAvailable, getProvider } from '../utils';
13
+ import { type SafeRpcUrl } from '../types';
13
14
 
14
15
  export const validateAddressSignature = async ({
15
16
  address,
16
17
  token,
17
18
  data,
18
19
  chainId,
20
+ safeRpcUrl,
19
21
  }: {
20
22
  address: string;
21
23
  token: string;
22
24
  data: TypedMessage<any>;
23
25
  chainId: number;
26
+ safeRpcUrl?: SafeRpcUrl;
24
27
  }): Promise<boolean> => {
25
28
  try {
26
- const provider = getProvider(chainId); // [TODO Hanan], should expect it from signer.provider, or passed in client
29
+ const provider = getProvider(chainId, safeRpcUrl);
27
30
  if (provider) {
28
31
  const contractAddress = await isContractAvailable(address, provider);
29
32
  if (contractAddress) {
@@ -32,6 +35,7 @@ export const validateAddressSignature = async ({
32
35
  data: data as unknown as EIP712TypedData,
33
36
  address,
34
37
  chainId,
38
+ safeRpcUrl,
35
39
  });
36
40
  }
37
41
  }
@@ -69,16 +73,19 @@ export const validateSmartContractSignature = async ({
69
73
  data,
70
74
  address,
71
75
  chainId,
76
+ safeRpcUrl,
72
77
  }: {
73
78
  token: string;
74
79
  data: EIP712TypedData;
75
80
  address: string;
76
81
  chainId: number;
82
+ safeRpcUrl?: SafeRpcUrl;
77
83
  }): Promise<boolean> => {
78
84
  try {
79
- const provider = PROVIDER_MAP[chainId];
85
+ const safeProvider = safeRpcUrl ?? PROVIDER_MAP[chainId];
86
+
80
87
  const protocolKit = await Safe.init({
81
- provider,
88
+ provider: safeProvider,
82
89
  safeAddress: address,
83
90
  });
84
91
  const messageHash = hashTypedData(data);