@obolnetwork/obol-sdk 2.8.0 → 2.8.1

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.
@@ -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
13
  import { OVMFactoryContract } 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);
693
659
 
694
- return response.splitAddress;
660
+ const predictedAddress = await splitV2FactoryContract[
661
+ 'predictDeterministicAddress((address[],uint256[],uint256,uint16),address,bytes32)'
662
+ ](splitParams, splitOwnerAddress, salt);
663
+
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];
@@ -87,7 +87,7 @@ export class ObolSplits {
87
87
  }
88
88
 
89
89
  const chainConfig = CHAIN_CONFIGURATION[this.chainId];
90
- if (!chainConfig?.OVM_FACTORY_ADDRESS) {
90
+ if (!chainConfig?.OVM_FACTORY_CONTRACT) {
91
91
  throw new Error(
92
92
  `OVM contract factory is not configured for chain ${this.chainId}`,
93
93
  );
@@ -98,28 +98,53 @@ export class ObolSplits {
98
98
  'Provider is required to check OVM factory contract availability',
99
99
  );
100
100
  }
101
-
102
- const ovmFactoryConfig = chainConfig.OVM_FACTORY_ADDRESS;
103
- if (!ovmFactoryConfig?.address || !ovmFactoryConfig?.bytecode) {
101
+ const ovmFactoryConfig = chainConfig.OVM_FACTORY_CONTRACT;
102
+ const splitV2FactoryConfig = chainConfig.SPLIT_V2_FACTORY_CONTRACT;
103
+ const multiCallConfig = chainConfig.MULTICALL_CONTRACT;
104
+
105
+ if (
106
+ !ovmFactoryConfig?.address ||
107
+ !ovmFactoryConfig?.bytecode ||
108
+ !splitV2FactoryConfig?.address ||
109
+ !splitV2FactoryConfig?.bytecode ||
110
+ !multiCallConfig?.address ||
111
+ !multiCallConfig?.bytecode
112
+ ) {
104
113
  throw new Error(
105
- `OVM factory contract configuration is incomplete for chain ${this.chainId}`,
114
+ `Contracts configuration is incomplete for chain ${this.chainId}`,
106
115
  );
107
116
  }
108
117
 
109
- const checkOVMFactoryAddress = await isContractAvailable(
118
+ const checkOVMFactoryContract = await isContractAvailable(
110
119
  ovmFactoryConfig.address,
111
120
  this.provider,
112
121
  ovmFactoryConfig.bytecode,
113
122
  );
114
123
 
115
- if (!checkOVMFactoryAddress) {
124
+ const checkSplitV2FactoryContract = await isContractAvailable(
125
+ splitV2FactoryConfig.address,
126
+ this.provider,
127
+ splitV2FactoryConfig.bytecode,
128
+ );
129
+
130
+ const checkMultiCallContract = await isContractAvailable(
131
+ multiCallConfig.address,
132
+ this.provider,
133
+ multiCallConfig.bytecode,
134
+ );
135
+
136
+ if (
137
+ !checkOVMFactoryContract ||
138
+ !checkSplitV2FactoryContract ||
139
+ !checkMultiCallContract
140
+ ) {
116
141
  throw new Error(
117
- `OVM factory contract is not deployed or available on chain ${this.chainId}`,
142
+ `Splitter contract is not deployed or available on chain ${this.chainId}`,
118
143
  );
119
144
  }
120
145
 
121
146
  const retroActiveFundingRecipient = {
122
- address: chainConfig.RETROACTIVE_FUNDING_ADDRESS.address,
147
+ address: chainConfig.RETROACTIVE_FUNDING_CONTRACT.address,
123
148
  percentAllocation: DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
124
149
  };
125
150
 
@@ -228,7 +253,7 @@ export class ObolSplits {
228
253
  }
229
254
 
230
255
  const chainConfig = CHAIN_CONFIGURATION[this.chainId];
231
- if (!chainConfig?.OVM_FACTORY_ADDRESS) {
256
+ if (!chainConfig?.OVM_FACTORY_CONTRACT) {
232
257
  throw new Error(
233
258
  `OVM contract factory is not configured for chain ${this.chainId}`,
234
259
  );
@@ -240,27 +265,53 @@ export class ObolSplits {
240
265
  );
241
266
  }
242
267
 
243
- const ovmFactoryConfig = chainConfig.OVM_FACTORY_ADDRESS;
244
- if (!ovmFactoryConfig?.address || !ovmFactoryConfig?.bytecode) {
268
+ const ovmFactoryConfig = chainConfig.OVM_FACTORY_CONTRACT;
269
+ const splitV2FactoryConfig = chainConfig.SPLIT_V2_FACTORY_CONTRACT;
270
+ const multiCallConfig = chainConfig.MULTICALL_CONTRACT;
271
+
272
+ if (
273
+ !ovmFactoryConfig?.address ||
274
+ !ovmFactoryConfig?.bytecode ||
275
+ !splitV2FactoryConfig?.address ||
276
+ !splitV2FactoryConfig?.bytecode ||
277
+ !multiCallConfig?.address ||
278
+ !multiCallConfig?.bytecode
279
+ ) {
245
280
  throw new Error(
246
- `OVM factory contract configuration is incomplete for chain ${this.chainId}`,
281
+ `Contracts configuration is incomplete for chain ${this.chainId}`,
247
282
  );
248
283
  }
249
284
 
250
- const checkOVMFactoryAddress = await isContractAvailable(
285
+ const checkOVMFactoryContract = await isContractAvailable(
251
286
  ovmFactoryConfig.address,
252
287
  this.provider,
253
288
  ovmFactoryConfig.bytecode,
254
289
  );
255
290
 
256
- if (!checkOVMFactoryAddress) {
291
+ const checkSplitV2FactoryContract = await isContractAvailable(
292
+ splitV2FactoryConfig.address,
293
+ this.provider,
294
+ splitV2FactoryConfig.bytecode,
295
+ );
296
+
297
+ const checkMultiCallContract = await isContractAvailable(
298
+ multiCallConfig.address,
299
+ this.provider,
300
+ multiCallConfig.bytecode,
301
+ );
302
+
303
+ if (
304
+ !checkOVMFactoryContract ||
305
+ !checkSplitV2FactoryContract ||
306
+ !checkMultiCallContract
307
+ ) {
257
308
  throw new Error(
258
- `OVM factory contract is not deployed or available on chain ${this.chainId}`,
309
+ `Splitter contract is not deployed or available on chain ${this.chainId}`,
259
310
  );
260
311
  }
261
312
 
262
313
  const retroActiveFundingRecipient = {
263
- address: chainConfig.RETROACTIVE_FUNDING_ADDRESS.address,
314
+ address: chainConfig.RETROACTIVE_FUNDING_CONTRACT.address,
264
315
  percentAllocation: DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
265
316
  };
266
317
 
package/src/types.ts CHANGED
@@ -578,27 +578,31 @@ export type OVMArgs = {
578
578
  };
579
579
 
580
580
  export type ChainConfig = {
581
- SPLITMAIN_ADDRESS: {
581
+ SPLITMAIN_CONTRACT: {
582
582
  address: string;
583
583
  bytecode: string;
584
584
  };
585
- MULTICALL_ADDRESS: {
585
+ MULTICALL_CONTRACT: {
586
586
  address: string;
587
587
  bytecode: string;
588
588
  };
589
- OWR_FACTORY_ADDRESS: {
589
+ OWR_FACTORY_CONTRACT: {
590
590
  address: string;
591
591
  bytecode: string;
592
592
  };
593
- RETROACTIVE_FUNDING_ADDRESS: {
593
+ RETROACTIVE_FUNDING_CONTRACT: {
594
594
  address: string;
595
595
  bytecode: string;
596
596
  };
597
- OVM_FACTORY_ADDRESS?: {
597
+ OVM_FACTORY_CONTRACT?: {
598
598
  address: string;
599
599
  bytecode: string;
600
600
  };
601
- WAREHOUSE_ADDRESS?: {
601
+ WAREHOUSE_CONTRACT?: {
602
+ address: string;
603
+ bytecode: string;
604
+ };
605
+ SPLIT_V2_FACTORY_CONTRACT?: {
602
606
  address: string;
603
607
  bytecode: string;
604
608
  };