@obolnetwork/obol-sdk 2.8.0 → 2.9.0

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 (40) hide show
  1. package/dist/cjs/package.json +2 -4
  2. package/dist/cjs/src/abi/splitV2FactoryAbi.js +420 -0
  3. package/dist/cjs/src/ajv.js +24 -0
  4. package/dist/cjs/src/bytecodes.js +3 -1
  5. package/dist/cjs/src/constants.js +24 -16
  6. package/dist/cjs/src/index.js +10 -10
  7. package/dist/cjs/src/schema.js +29 -3
  8. package/dist/cjs/src/splits/splitHelpers.js +100 -109
  9. package/dist/cjs/src/splits/splits.js +77 -16
  10. package/dist/cjs/test/client/ajv.spec.js +32 -0
  11. package/dist/cjs/test/splits/splits.spec.js +76 -2
  12. package/dist/esm/package.json +2 -4
  13. package/dist/esm/src/abi/splitV2FactoryAbi.js +417 -0
  14. package/dist/esm/src/ajv.js +24 -0
  15. package/dist/esm/src/bytecodes.js +2 -0
  16. package/dist/esm/src/constants.js +25 -17
  17. package/dist/esm/src/index.js +10 -10
  18. package/dist/esm/src/schema.js +28 -2
  19. package/dist/esm/src/splits/splitHelpers.js +101 -110
  20. package/dist/esm/src/splits/splits.js +79 -18
  21. package/dist/esm/test/client/ajv.spec.js +33 -1
  22. package/dist/esm/test/splits/splits.spec.js +77 -3
  23. package/dist/types/src/abi/splitV2FactoryAbi.d.ts +60 -0
  24. package/dist/types/src/bytecodes.d.ts +2 -0
  25. package/dist/types/src/exits/verificationHelpers.d.ts +1 -0
  26. package/dist/types/src/schema.d.ts +30 -3
  27. package/dist/types/src/splits/splitHelpers.d.ts +17 -3
  28. package/dist/types/src/splits/splits.d.ts +28 -1
  29. package/dist/types/src/types.d.ts +21 -6
  30. package/dist/types/src/verification/common.d.ts +1 -0
  31. package/package.json +2 -4
  32. package/src/abi/splitV2FactoryAbi.ts +417 -0
  33. package/src/ajv.ts +33 -0
  34. package/src/bytecodes.ts +4 -0
  35. package/src/constants.ts +26 -16
  36. package/src/index.ts +14 -14
  37. package/src/schema.ts +29 -2
  38. package/src/splits/splitHelpers.ts +169 -124
  39. package/src/splits/splits.ts +115 -17
  40. package/src/types.ts +24 -6
package/src/constants.ts CHANGED
@@ -9,11 +9,13 @@ import {
9
9
  HOODI_MULTICALL_BYTECODE,
10
10
  HOODI_OVM_FACTORY_BYTECODE,
11
11
  HOODI_OWR_FACTORY_BYTECODE,
12
+ HOODI_SPLIT_V2_FACTORY_BYTECODE,
12
13
  HOODI_SPLITMAIN_BYTECODE,
13
14
  HOODI_WAREHOUSE_BYTECODE,
14
15
  MAINNET_MULTICALL_BYTECODE,
15
16
  MAINNET_OVM_FACTORY_BYTECODE,
16
17
  MAINNET_OWR_FACTORY_BYTECODE,
18
+ MAINNET_SPLIT_V2_FACTORY_BYTECODE,
17
19
  MAINNET_SPLITMAIN_BYTECODE,
18
20
  MAINNET_WAREHOUSE_BYTECODE,
19
21
  } from './bytecodes';
@@ -168,76 +170,84 @@ export const isChainSupportedForSplitters = (chainId: number): boolean => {
168
170
 
169
171
  export const CHAIN_CONFIGURATION: Record<number, ChainConfig> = {
170
172
  [FORK_MAPPING['0x00000000']]: {
171
- SPLITMAIN_ADDRESS: {
173
+ SPLITMAIN_CONTRACT: {
172
174
  address: '0x2ed6c4B5dA6378c7897AC67Ba9e43102Feb694EE',
173
175
  bytecode: MAINNET_SPLITMAIN_BYTECODE,
174
176
  },
175
- MULTICALL_ADDRESS: {
177
+ MULTICALL_CONTRACT: {
176
178
  address: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
177
179
  bytecode: MAINNET_MULTICALL_BYTECODE,
178
180
  },
179
- OWR_FACTORY_ADDRESS: {
181
+ OWR_FACTORY_CONTRACT: {
180
182
  address: '0x119acd7844cbdd5fc09b1c6a4408f490c8f7f522',
181
183
  bytecode: MAINNET_OWR_FACTORY_BYTECODE,
182
184
  },
183
- RETROACTIVE_FUNDING_ADDRESS: {
185
+ RETROACTIVE_FUNDING_CONTRACT: {
184
186
  address: '0xDe5aE4De36c966747Ea7DF13BD9589642e2B1D0d',
185
187
  bytecode: '',
186
188
  },
187
189
  // OVM and SplitV2 Contract Addresses
188
- OVM_FACTORY_ADDRESS: {
190
+ OVM_FACTORY_CONTRACT: {
189
191
  address: '0xdfe2d8b26806583cf03b3cb623b0752f8670e93e',
190
192
  bytecode: MAINNET_OVM_FACTORY_BYTECODE,
191
193
  },
192
- WAREHOUSE_ADDRESS: {
194
+ WAREHOUSE_CONTRACT: {
193
195
  address: '0x8fb66F38cF86A3d5e8768f8F1754A24A6c661Fb8',
194
196
  bytecode: MAINNET_WAREHOUSE_BYTECODE,
195
197
  },
198
+ SPLIT_V2_FACTORY_CONTRACT: {
199
+ address: '0x5cbA88D55Cec83caD5A105Ad40C8c9aF20bE21d1',
200
+ bytecode: MAINNET_SPLIT_V2_FACTORY_BYTECODE,
201
+ },
196
202
  },
197
203
  [FORK_MAPPING['0x01017000']]: {
198
- SPLITMAIN_ADDRESS: {
204
+ SPLITMAIN_CONTRACT: {
199
205
  address: '0xfC8a305728051367797DADE6Aa0344E0987f5286',
200
206
  bytecode: HOLESKY_SPLITMAIN_BYTECODE,
201
207
  },
202
- MULTICALL_ADDRESS: {
208
+ MULTICALL_CONTRACT: {
203
209
  address: '0xcA11bde05977b3631167028862bE2a173976CA11',
204
210
  bytecode: HOLESKY_MULTICALL_BYTECODE,
205
211
  },
206
- OWR_FACTORY_ADDRESS: {
212
+ OWR_FACTORY_CONTRACT: {
207
213
  address: '0xc0961353fcc43a99e3041db07ac646720e116256',
208
214
  bytecode: HOLESKY_OWR_FACTORY_BYTECODE,
209
215
  },
210
- RETROACTIVE_FUNDING_ADDRESS: {
216
+ RETROACTIVE_FUNDING_CONTRACT: {
211
217
  address: '0x43F641fA70e09f0326ac66b4Ef0C416EaEcBC6f5',
212
218
  bytecode: '',
213
219
  },
214
220
  },
215
221
  [FORK_MAPPING['0x10000910']]: {
216
- SPLITMAIN_ADDRESS: {
222
+ SPLITMAIN_CONTRACT: {
217
223
  address: '0xc05ae267291705ac16F75283572294ed2a91CBc7',
218
224
  bytecode: HOODI_SPLITMAIN_BYTECODE,
219
225
  },
220
- MULTICALL_ADDRESS: {
226
+ MULTICALL_CONTRACT: {
221
227
  address: '0xcA11bde05977b3631167028862bE2a173976CA11',
222
228
  bytecode: HOODI_MULTICALL_BYTECODE,
223
229
  },
224
- OWR_FACTORY_ADDRESS: {
230
+ OWR_FACTORY_CONTRACT: {
225
231
  address: '0x9ff0c649d0bf5fe7efa4d72e94bed7302ed5c8d7',
226
232
  bytecode: HOODI_OWR_FACTORY_BYTECODE,
227
233
  },
228
- RETROACTIVE_FUNDING_ADDRESS: {
234
+ RETROACTIVE_FUNDING_CONTRACT: {
229
235
  address: '0x43F641fA70e09f0326ac66b4Ef0C416EaEcBC6f5',
230
236
  bytecode: '',
231
237
  },
232
238
  // OVM and SplitV2 Contract Addresses
233
- OVM_FACTORY_ADDRESS: {
239
+ OVM_FACTORY_CONTRACT: {
234
240
  address: '0x6F13d929C783a420AE4DC71C1dcc27A02038Ed09',
235
241
  bytecode: HOODI_OVM_FACTORY_BYTECODE,
236
242
  },
237
- WAREHOUSE_ADDRESS: {
243
+ WAREHOUSE_CONTRACT: {
238
244
  address: '0x8fb66F38cF86A3d5e8768f8F1754A24A6c661Fb8',
239
245
  bytecode: HOODI_WAREHOUSE_BYTECODE,
240
246
  },
247
+ SPLIT_V2_FACTORY_CONTRACT: {
248
+ address: '0x5cbA88D55Cec83caD5A105Ad40C8c9aF20bE21d1',
249
+ bytecode: HOODI_SPLIT_V2_FACTORY_BYTECODE,
250
+ },
241
251
  },
242
252
  };
243
253
 
package/src/index.ts CHANGED
@@ -221,28 +221,28 @@ export class Client extends Base {
221
221
  }
222
222
 
223
223
  const chainConfig = CHAIN_CONFIGURATION[this.chainId];
224
- if (!chainConfig?.SPLITMAIN_ADDRESS) {
224
+ if (!chainConfig?.SPLITMAIN_CONTRACT) {
225
225
  throw new Error(
226
- `SPLITMAIN_ADDRESS is not configured for chain ${this.chainId}`,
226
+ `SPLITMAIN_CONTRACT is not configured for chain ${this.chainId}`,
227
227
  );
228
228
  }
229
229
 
230
230
  const checkSplitMainAddress = await isContractAvailable(
231
- chainConfig.SPLITMAIN_ADDRESS.address,
231
+ chainConfig.SPLITMAIN_CONTRACT.address,
232
232
  this.signer.provider as ProviderType,
233
- chainConfig.SPLITMAIN_ADDRESS.bytecode,
233
+ chainConfig.SPLITMAIN_CONTRACT.bytecode,
234
234
  );
235
235
 
236
236
  const checkMulticallAddress = await isContractAvailable(
237
- chainConfig.MULTICALL_ADDRESS.address,
237
+ chainConfig.MULTICALL_CONTRACT.address,
238
238
  this.signer.provider as ProviderType,
239
- chainConfig.MULTICALL_ADDRESS.bytecode,
239
+ chainConfig.MULTICALL_CONTRACT.bytecode,
240
240
  );
241
241
 
242
242
  const checkOWRFactoryAddress = await isContractAvailable(
243
- chainConfig.OWR_FACTORY_ADDRESS.address,
243
+ chainConfig.OWR_FACTORY_CONTRACT.address,
244
244
  this.signer.provider as ProviderType,
245
- chainConfig.OWR_FACTORY_ADDRESS.bytecode,
245
+ chainConfig.OWR_FACTORY_CONTRACT.bytecode,
246
246
  );
247
247
 
248
248
  if (
@@ -256,7 +256,7 @@ export class Client extends Base {
256
256
  }
257
257
 
258
258
  const retroActiveFundingRecipient = {
259
- account: chainConfig.RETROACTIVE_FUNDING_ADDRESS.address,
259
+ account: chainConfig.RETROACTIVE_FUNDING_CONTRACT.address,
260
260
  percentAllocation: validatedPayload.ObolRAFSplit,
261
261
  };
262
262
 
@@ -342,16 +342,16 @@ export class Client extends Base {
342
342
  }
343
343
 
344
344
  const chainConfig = CHAIN_CONFIGURATION[this.chainId];
345
- if (!chainConfig?.SPLITMAIN_ADDRESS) {
345
+ if (!chainConfig?.SPLITMAIN_CONTRACT) {
346
346
  throw new Error(
347
- `SPLITMAIN_ADDRESS is not configured for chain ${this.chainId}`,
347
+ `SPLITMAIN_CONTRACT is not configured for chain ${this.chainId}`,
348
348
  );
349
349
  }
350
350
 
351
351
  const checkSplitMainAddress = await isContractAvailable(
352
- chainConfig.SPLITMAIN_ADDRESS.address,
352
+ chainConfig.SPLITMAIN_CONTRACT.address,
353
353
  this.signer.provider as ProviderType,
354
- chainConfig.SPLITMAIN_ADDRESS.bytecode,
354
+ chainConfig.SPLITMAIN_CONTRACT.bytecode,
355
355
  );
356
356
 
357
357
  if (!checkSplitMainAddress) {
@@ -361,7 +361,7 @@ export class Client extends Base {
361
361
  }
362
362
 
363
363
  const retroActiveFundingRecipient = {
364
- account: chainConfig.RETROACTIVE_FUNDING_ADDRESS.address,
364
+ account: chainConfig.RETROACTIVE_FUNDING_CONTRACT.address,
365
365
  percentAllocation: validatedPayload.ObolRAFSplit,
366
366
  };
367
367
 
package/src/schema.ts CHANGED
@@ -5,7 +5,6 @@ import {
5
5
  PRINCIPAL_THRESHOLD,
6
6
  } from './constants';
7
7
  import { VALID_DEPOSIT_AMOUNTS, VALID_NON_COMPOUNDING_AMOUNTS } from './ajv';
8
- import { zeroAddress } from 'viem';
9
8
 
10
9
  export const operatorPayloadSchema = {
11
10
  type: 'object',
@@ -175,7 +174,7 @@ export const ovmBaseSplitPayload = {
175
174
  splitOwnerAddress: {
176
175
  type: 'string',
177
176
  pattern: '^0x[a-fA-F0-9]{40}$',
178
- default: zeroAddress,
177
+ default: ZeroAddress,
179
178
  },
180
179
  principalThreshold: {
181
180
  type: 'number',
@@ -230,3 +229,31 @@ export const ovmTotalSplitPayloadSchema = {
230
229
  'OVMOwnerAddress',
231
230
  ],
232
231
  };
232
+
233
+ export const ovmRequestWithdrawalPayloadSchema = {
234
+ type: 'object',
235
+ properties: {
236
+ ovmAddress: {
237
+ type: 'string',
238
+ pattern: '^0x[a-fA-F0-9]{40}$',
239
+ },
240
+ pubKeys: {
241
+ type: 'array',
242
+ minItems: 1,
243
+ items: {
244
+ type: 'string',
245
+ pattern: '^0x[a-fA-F0-9]{96}$', // 48 bytes = 96 hex chars + 0x prefix
246
+ },
247
+ },
248
+ amounts: {
249
+ type: 'array',
250
+ minItems: 1,
251
+ items: {
252
+ type: 'string',
253
+ pattern: '^[0-9]+$',
254
+ },
255
+ },
256
+ },
257
+ validateOVMRequestWithdrawalPayload: true,
258
+ required: ['ovmAddress', 'pubKeys', 'amounts'],
259
+ };
@@ -8,27 +8,18 @@ import {
8
8
  type OVMArgs,
9
9
  type ChainConfig,
10
10
  } from '../types';
11
- import {
12
- BrowserProvider,
13
- Contract,
14
- Interface,
15
- parseEther,
16
- type Wallet,
17
- ZeroAddress,
18
- } from 'ethers';
11
+ import { Contract, Interface, parseEther, ZeroAddress } from 'ethers';
19
12
  import { OWRContract, OWRFactoryContract } from '../abi/OWR';
20
- import { OVMFactoryContract } from '../abi/OVMFactory';
13
+ import { OVMFactoryContract, OVMContract } from '../abi/OVMFactory';
21
14
  import { splitMainEthereumAbi } from '../abi/SplitMain';
22
15
  import { MultiCallContract } from '../abi/Multicall';
23
- import { CHAIN_CONFIGURATION, CHAIN_PUBLIC_RPC_URL } from '../constants';
24
- import { SplitsClient } from '@0xsplits/splits-sdk';
25
- import { createPublicClient, createWalletClient, custom, http } from 'viem';
26
- import { hoodi, mainnet } from 'viem/chains';
27
- import { privateKeyToAccount } from 'viem/accounts';
16
+ import { CHAIN_CONFIGURATION } from '../constants';
17
+ import { splitV2FactoryAbi } from '../abi/splitV2FactoryAbi';
28
18
 
29
19
  const splitMainContractInterface = new Interface(splitMainEthereumAbi);
30
20
  const owrFactoryContractInterface = new Interface(OWRFactoryContract.abi);
31
21
  const ovmFactoryContractInterface = new Interface(OVMFactoryContract.abi);
22
+ const splitV2FactoryInterface = new Interface(splitV2FactoryAbi);
32
23
 
33
24
  type Call = {
34
25
  target: ETH_ADDRESS;
@@ -104,7 +95,7 @@ export const predictSplitterAddress = async ({
104
95
  }): Promise<ETH_ADDRESS> => {
105
96
  try {
106
97
  const splitMainContractInstance = new Contract(
107
- getChainConfig(chainId).SPLITMAIN_ADDRESS.address,
98
+ getChainConfig(chainId).SPLITMAIN_CONTRACT.address,
108
99
  splitMainEthereumAbi,
109
100
  signer,
110
101
  );
@@ -258,7 +249,7 @@ const createOWRContract = async ({
258
249
  }): Promise<ETH_ADDRESS> => {
259
250
  try {
260
251
  const OWRFactoryInstance = new Contract(
261
- getChainConfig(chainId).OWR_FACTORY_ADDRESS.address,
252
+ getChainConfig(chainId).OWR_FACTORY_CONTRACT.address,
262
253
  OWRFactoryContract.abi,
263
254
  signer,
264
255
  );
@@ -345,7 +336,7 @@ export const deploySplitterContract = async ({
345
336
  }): Promise<ETH_ADDRESS> => {
346
337
  try {
347
338
  const splitMainContractInstance = new Contract(
348
- getChainConfig(chainId).SPLITMAIN_ADDRESS.address,
339
+ getChainConfig(chainId).SPLITMAIN_CONTRACT.address,
349
340
  splitMainEthereumAbi,
350
341
  signer,
351
342
  );
@@ -444,21 +435,16 @@ export const deploySplitterAndOWRContracts = async ({
444
435
 
445
436
  executeCalls.push(
446
437
  {
447
- target: getChainConfig(chainId).SPLITMAIN_ADDRESS.address,
438
+ target: getChainConfig(chainId).SPLITMAIN_CONTRACT.address,
448
439
  callData: splitTxData,
449
440
  },
450
441
  {
451
- target: getChainConfig(chainId).OWR_FACTORY_ADDRESS.address,
442
+ target: getChainConfig(chainId).OWR_FACTORY_CONTRACT.address,
452
443
  callData: owrTxData,
453
444
  },
454
445
  );
455
- const multicallAddess = getChainConfig(chainId).MULTICALL_ADDRESS.address;
456
446
 
457
- const executeMultiCalls = await multicall(
458
- executeCalls,
459
- signer,
460
- multicallAddess,
461
- );
447
+ const executeMultiCalls = await multicall(executeCalls, signer, chainId);
462
448
 
463
449
  const splitAddressData = executeMultiCalls?.logs[0]?.topics[1];
464
450
  const formattedSplitterAddress = '0x' + splitAddressData?.slice(26, 66);
@@ -519,9 +505,11 @@ export const getOWRTranches = async ({
519
505
  export const multicall = async (
520
506
  calls: Call[],
521
507
  signer: SignerType,
522
- multicallAddress: string,
508
+ chainId: number,
523
509
  ): Promise<any> => {
524
510
  try {
511
+ const chainConfig = getChainConfig(chainId);
512
+ const multicallAddress = chainConfig.MULTICALL_CONTRACT.address;
525
513
  const multiCallContractInstance = new Contract(
526
514
  multicallAddress,
527
515
  MultiCallContract.abi,
@@ -613,57 +601,31 @@ export const formatRecipientsForSplitV2 = (
613
601
  }));
614
602
  };
615
603
 
616
- export const createSplitsClient = async (
617
- signer: SignerType,
618
- chainId: number,
619
- ): Promise<SplitsClient> => {
620
- const chain = chainId === 1 ? mainnet : hoodi;
621
- const client = createPublicClient({
622
- chain,
623
- transport: http(CHAIN_PUBLIC_RPC_URL[chainId]),
624
- });
625
- // convert signer to walletClient
626
- if (
627
- typeof window !== 'undefined' &&
628
- signer.provider instanceof BrowserProvider
629
- ) {
630
- // For browser environment
631
- const address = await signer.getAddress();
632
- const viemWalletClient = createWalletClient({
633
- account: address,
634
- chain,
635
- transport: custom((window as any).ethereum),
636
- });
637
- return new SplitsClient({
638
- publicClient: client,
639
- walletClient: viemWalletClient,
640
- chainId,
641
- });
642
- } else {
643
- // For non-browser environment, extract private key from signer
644
- const signerPrivateKey = (signer as Wallet)?.privateKey;
645
- if (!signerPrivateKey) {
646
- throw new Error('Signer private key not available');
647
- }
648
-
649
- const privateKey = signerPrivateKey.startsWith('0x')
650
- ? signerPrivateKey
651
- : `0x${signerPrivateKey}`;
652
-
653
- const scriptAccount = privateKeyToAccount(privateKey as `0x${string}`);
654
- const account = scriptAccount;
604
+ // Helper function to create SplitV2 parameters
605
+ const createSplitV2Params = (
606
+ recipients: SplitV2Recipient[],
607
+ distributorFeePercent: number,
608
+ ): {
609
+ recipients: string[];
610
+ allocations: number[];
611
+ totalAllocation: number;
612
+ distributionIncentive: number;
613
+ } => {
614
+ const addresses = recipients.map(r => r.address);
615
+ const allocations = recipients.map(r =>
616
+ Math.floor(r.percentAllocation * 1e4),
617
+ ); // Convert to basis points
618
+ const totalAllocation = allocations.reduce(
619
+ (sum, allocation) => sum + allocation,
620
+ 0,
621
+ );
655
622
 
656
- const viemWalletClient = createWalletClient({
657
- account,
658
- chain,
659
- transport: http(CHAIN_PUBLIC_RPC_URL[chainId]),
660
- });
661
- return new SplitsClient({
662
- publicClient: client,
663
- walletClient: viemWalletClient,
664
- chainId,
665
- });
666
- }
623
+ return {
624
+ recipients: addresses,
625
+ allocations,
626
+ totalAllocation,
627
+ distributionIncentive: distributorFeePercent,
628
+ };
667
629
  };
668
630
 
669
631
  export const predictSplitV2Address = async ({
@@ -682,19 +644,27 @@ export const predictSplitV2Address = async ({
682
644
  chainId: number;
683
645
  }): Promise<string> => {
684
646
  try {
685
- const splitsClient = await createSplitsClient(signer, chainId);
647
+ const chainConfig = getChainConfig(chainId);
648
+ if (!chainConfig?.SPLIT_V2_FACTORY_CONTRACT?.address) {
649
+ throw new Error(`SplitV2 Factory not configured for chain ${chainId}`);
650
+ }
686
651
 
687
- const response = await splitsClient.splitV2.predictDeterministicAddress({
688
- ownerAddress: splitOwnerAddress,
689
- recipients,
690
- distributorFeePercent,
691
- salt,
692
- });
652
+ const splitV2FactoryContract = new Contract(
653
+ chainConfig.SPLIT_V2_FACTORY_CONTRACT.address,
654
+ splitV2FactoryAbi,
655
+ signer,
656
+ );
657
+
658
+ const splitParams = createSplitV2Params(recipients, distributorFeePercent);
659
+
660
+ const predictedAddress = await splitV2FactoryContract[
661
+ 'predictDeterministicAddress((address[],uint256[],uint256,uint16),address,bytes32)'
662
+ ](splitParams, splitOwnerAddress, salt);
693
663
 
694
- return response.splitAddress;
664
+ return predictedAddress;
695
665
  } catch (error: any) {
696
666
  throw new Error(
697
- `Failed to predict SplitV2 address: ${error.message ?? 'SplitV2 SDK call failed'}`,
667
+ `Failed to predict SplitV2 address: ${error.message ?? 'SplitV2 contract call failed'}`,
698
668
  );
699
669
  }
700
670
  };
@@ -715,15 +685,26 @@ export const isSplitV2Deployed = async ({
715
685
  chainId: number;
716
686
  }): Promise<boolean> => {
717
687
  try {
718
- const splitsClient = await createSplitsClient(signer, chainId);
719
- const response = await splitsClient.splitV2.isDeployed({
720
- ownerAddress: splitOwnerAddress,
721
- recipients,
722
- distributorFeePercent,
688
+ const chainConfig = getChainConfig(chainId);
689
+ if (!chainConfig?.SPLIT_V2_FACTORY_CONTRACT?.address) {
690
+ throw new Error(`SplitV2 Factory not configured for chain ${chainId}`);
691
+ }
692
+
693
+ const splitV2FactoryContract = new Contract(
694
+ chainConfig.SPLIT_V2_FACTORY_CONTRACT.address,
695
+ splitV2FactoryAbi,
696
+ signer,
697
+ );
698
+
699
+ const splitParams = createSplitV2Params(recipients, distributorFeePercent);
700
+
701
+ const [, exists] = await splitV2FactoryContract.isDeployed(
702
+ splitParams,
703
+ splitOwnerAddress,
723
704
  salt,
724
- });
705
+ );
725
706
 
726
- return response.deployed;
707
+ return exists;
727
708
  } catch (error: any) {
728
709
  // If the check fails, assume it's not deployed
729
710
  return false;
@@ -747,12 +728,12 @@ export const deployOVMContract = async ({
747
728
  }): Promise<string> => {
748
729
  try {
749
730
  const chainConfig = getChainConfig(chainId);
750
- if (!chainConfig?.OVM_FACTORY_ADDRESS?.address) {
731
+ if (!chainConfig?.OVM_FACTORY_CONTRACT?.address) {
751
732
  throw new Error(`OVM Factory not configured for chain ${chainId}`);
752
733
  }
753
734
 
754
735
  const ovmFactoryContract = new Contract(
755
- chainConfig.OVM_FACTORY_ADDRESS.address,
736
+ chainConfig.OVM_FACTORY_CONTRACT.address,
756
737
  OVMFactoryContract.abi,
757
738
  signer,
758
739
  );
@@ -806,36 +787,49 @@ export const deployOVMAndSplitV2 = async ({
806
787
  }): Promise<string> => {
807
788
  try {
808
789
  const chainConfig = getChainConfig(chainId);
809
- if (!chainConfig?.OVM_FACTORY_ADDRESS?.address) {
790
+ if (!chainConfig?.OVM_FACTORY_CONTRACT?.address) {
810
791
  throw new Error(`OVM Factory not configured for chain ${chainId}`);
811
792
  }
812
793
 
813
- const splitsClient = await createSplitsClient(signer, chainId);
814
- const executeCalls: any[] = [];
794
+ if (!chainConfig?.SPLIT_V2_FACTORY_CONTRACT?.address) {
795
+ throw new Error(`SplitV2 Factory not configured for chain ${chainId}`);
796
+ }
797
+
798
+ const executeCalls: Call[] = [];
815
799
 
816
800
  if (rewardRecipients && !isRewardsSplitterDeployed) {
817
801
  // Create rewards split call data
818
- const rewardsSplitTxData =
819
- await splitsClient.splitV2.callData.createSplit({
820
- recipients: rewardRecipients,
821
- distributorFeePercent,
822
- ownerAddress: splitOwnerAddress,
823
- salt,
824
- });
802
+ const splitParams = createSplitV2Params(
803
+ rewardRecipients,
804
+ distributorFeePercent,
805
+ );
806
+ const rewardsSplitTxData = encodeCreateSplitV2DeterministicTxData(
807
+ splitParams,
808
+ splitOwnerAddress,
809
+ salt,
810
+ );
825
811
 
826
- executeCalls.push(rewardsSplitTxData);
812
+ executeCalls.push({
813
+ target: chainConfig.SPLIT_V2_FACTORY_CONTRACT.address,
814
+ callData: rewardsSplitTxData,
815
+ });
827
816
  }
828
817
 
829
818
  // Create principal split call data if needed (for total split scenario)
830
819
  if (principalSplitRecipients && !isPrincipalSplitDeployed) {
831
- const principalSplitTxData =
832
- await splitsClient.splitV2.callData.createSplit({
833
- recipients: principalSplitRecipients,
834
- distributorFeePercent,
835
- salt,
836
- ownerAddress: splitOwnerAddress,
837
- });
838
- executeCalls.push(principalSplitTxData);
820
+ const principalSplitParams = createSplitV2Params(
821
+ principalSplitRecipients,
822
+ distributorFeePercent,
823
+ );
824
+ const principalSplitTxData = encodeCreateSplitV2DeterministicTxData(
825
+ principalSplitParams,
826
+ splitOwnerAddress,
827
+ salt,
828
+ );
829
+ executeCalls.push({
830
+ target: chainConfig.SPLIT_V2_FACTORY_CONTRACT.address,
831
+ callData: principalSplitTxData,
832
+ });
839
833
  }
840
834
 
841
835
  // Create OVM call data
@@ -847,25 +841,23 @@ export const deployOVMAndSplitV2 = async ({
847
841
  );
848
842
 
849
843
  executeCalls.push({
850
- address: chainConfig.OVM_FACTORY_ADDRESS.address,
851
- data: ovmTxData,
844
+ target: chainConfig.OVM_FACTORY_CONTRACT.address,
845
+ callData: ovmTxData,
852
846
  });
853
847
 
854
848
  // Execute multicall
855
- const executeMultiCalls = await splitsClient.splitV2.multicall({
856
- calls: executeCalls,
857
- });
849
+ const executeMultiCalls = await multicall(executeCalls, signer, chainId);
858
850
 
859
851
  // Extract addresses from events
860
852
  let ovmAddress: string | undefined;
861
- const eventCount = executeMultiCalls?.events.length;
853
+ const logsCount = executeMultiCalls?.logs?.length || 0;
862
854
 
863
- if (eventCount === 2) {
864
- ovmAddress = executeMultiCalls?.events[0]?.address;
865
- } else if (eventCount === 5) {
866
- ovmAddress = executeMultiCalls?.events[3]?.address;
855
+ if (logsCount === 2) {
856
+ ovmAddress = '0x' + executeMultiCalls?.logs[1]?.topics[1]?.slice(26, 66);
857
+ } else if (logsCount === 5) {
858
+ ovmAddress = '0x' + executeMultiCalls?.logs[4]?.topics[1]?.slice(26, 66);
867
859
  } else {
868
- ovmAddress = executeMultiCalls?.events[6]?.address;
860
+ ovmAddress = '0x' + executeMultiCalls?.logs[7]?.topics[1]?.slice(26, 66);
869
861
  }
870
862
  if (!ovmAddress) {
871
863
  throw new Error(
@@ -893,6 +885,23 @@ const encodeCreateOVMTxData = (
893
885
  );
894
886
  };
895
887
 
888
+ const encodeCreateSplitV2DeterministicTxData = (
889
+ splitParams: {
890
+ recipients: string[];
891
+ allocations: number[];
892
+ totalAllocation: number;
893
+ distributionIncentive: number;
894
+ },
895
+ splitOwnerAddress: string,
896
+ salt: `0x${string}`,
897
+ ): string => {
898
+ // creatorAddress can be kept as default https://docs.splits.org/sdk/splits-v2#createsplit
899
+ return splitV2FactoryInterface.encodeFunctionData(
900
+ 'createSplitDeterministic',
901
+ [splitParams, splitOwnerAddress, ZeroAddress, salt],
902
+ );
903
+ };
904
+
896
905
  // Helper function to safely get chain configuration
897
906
  const getChainConfig = (chainId: number): ChainConfig => {
898
907
  const config = CHAIN_CONFIGURATION[chainId];
@@ -901,3 +910,39 @@ const getChainConfig = (chainId: number): ChainConfig => {
901
910
  }
902
911
  return config;
903
912
  };
913
+
914
+ /**
915
+ * Requests withdrawal from an OVM contract
916
+ * @param ovmAddress - The address of the OVM contract
917
+ * @param pubKeys - Array of validator public keys in bytes format
918
+ * @param amounts - Array of withdrawal amounts in wei (uint64)
919
+ * @param signer - The signer to use for the transaction
920
+ * @returns Promise that resolves to the transaction hash
921
+ */
922
+ export const requestWithdrawalFromOVM = async ({
923
+ ovmAddress,
924
+ pubKeys,
925
+ amounts,
926
+ signer,
927
+ }: {
928
+ ovmAddress: string;
929
+ pubKeys: string[];
930
+ amounts: string[];
931
+ signer: SignerType;
932
+ }): Promise<{ txHash: string }> => {
933
+ try {
934
+ // Convert string amounts to bigint
935
+ const bigintAmounts = amounts.map(amount => BigInt(amount));
936
+
937
+ const ovmContract = new Contract(ovmAddress, OVMContract.abi, signer);
938
+
939
+ const tx = await ovmContract.requestWithdrawal(pubKeys, bigintAmounts);
940
+ const receipt = await tx.wait();
941
+
942
+ return { txHash: receipt.hash };
943
+ } catch (error: any) {
944
+ throw new Error(
945
+ `Failed to request withdrawal from OVM: ${error.message ?? 'Request withdrawal failed'}`,
946
+ );
947
+ }
948
+ };