@obolnetwork/obol-sdk 2.7.0 → 2.8.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.
- package/dist/cjs/package.json +4 -2
- package/dist/cjs/src/abi/OVMFactory.js +620 -0
- package/dist/cjs/src/ajv.js +33 -6
- package/dist/cjs/src/bytecodes.js +8 -1
- package/dist/cjs/src/constants.js +55 -7
- package/dist/cjs/src/index.js +21 -9
- package/dist/cjs/src/schema.js +71 -1
- package/dist/cjs/src/splits/splitHelpers.js +218 -12
- package/dist/cjs/src/splits/splits.js +261 -0
- package/dist/cjs/src/types.js +0 -1
- package/dist/cjs/test/client/ajv.spec.js +269 -0
- package/dist/cjs/test/client/methods.spec.js +418 -0
- package/dist/cjs/test/fixtures.js +13 -1
- package/dist/cjs/test/splits/splits.spec.js +239 -0
- package/dist/esm/package.json +4 -2
- package/dist/esm/src/abi/OVMFactory.js +617 -0
- package/dist/esm/src/ajv.js +33 -6
- package/dist/esm/src/bytecodes.js +7 -0
- package/dist/esm/src/constants.js +54 -7
- package/dist/esm/src/index.js +20 -9
- package/dist/esm/src/schema.js +71 -1
- package/dist/esm/src/splits/splitHelpers.js +213 -13
- package/dist/esm/src/splits/splits.js +257 -0
- package/dist/esm/src/types.js +0 -1
- package/dist/esm/test/client/ajv.spec.js +267 -0
- package/dist/esm/test/client/methods.spec.js +393 -0
- package/dist/esm/test/fixtures.js +12 -0
- package/dist/esm/test/splits/splits.spec.js +237 -0
- package/dist/types/src/abi/OVMFactory.d.ts +662 -0
- package/dist/types/src/bytecodes.d.ts +7 -0
- package/dist/types/src/constants.d.ts +10 -21
- package/dist/types/src/index.d.ts +7 -0
- package/dist/types/src/schema.d.ts +145 -0
- package/dist/types/src/splits/splitHelpers.d.ts +40 -1
- package/dist/types/src/splits/splits.d.ts +51 -0
- package/dist/types/src/types.d.ts +83 -2
- package/dist/types/test/client/ajv.spec.d.ts +1 -0
- package/dist/types/test/client/methods.spec.d.ts +1 -0
- package/dist/types/test/fixtures.d.ts +10 -0
- package/dist/types/test/splits/splits.spec.d.ts +1 -0
- package/package.json +4 -2
- package/src/abi/OVMFactory.ts +617 -0
- package/src/ajv.ts +55 -13
- package/src/bytecodes.ts +15 -0
- package/src/constants.ts +66 -8
- package/src/index.ts +36 -15
- package/src/schema.ts +80 -0
- package/src/splits/splitHelpers.ts +360 -14
- package/src/splits/splits.ts +355 -0
- package/src/types.ts +96 -3
package/dist/esm/src/schema.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ZeroAddress } from 'ethers';
|
|
2
|
-
import { DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT, DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT, } from './constants';
|
|
2
|
+
import { DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT, DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT, PRINCIPAL_THRESHOLD, } from './constants';
|
|
3
3
|
import { VALID_DEPOSIT_AMOUNTS, VALID_NON_COMPOUNDING_AMOUNTS } from './ajv';
|
|
4
|
+
import { zeroAddress } from 'viem';
|
|
4
5
|
export const operatorPayloadSchema = {
|
|
5
6
|
type: 'object',
|
|
6
7
|
properties: {
|
|
@@ -137,3 +138,72 @@ export const rewardsSplitterPayloadSchema = {
|
|
|
137
138
|
validateRewardsSplitRecipients: true,
|
|
138
139
|
required: ['splitRecipients', 'principalRecipient', 'etherAmount'],
|
|
139
140
|
};
|
|
141
|
+
export const ovmBaseSplitPayload = {
|
|
142
|
+
rewardSplitRecipients: {
|
|
143
|
+
type: 'array',
|
|
144
|
+
items: {
|
|
145
|
+
type: 'object',
|
|
146
|
+
properties: {
|
|
147
|
+
address: {
|
|
148
|
+
type: 'string',
|
|
149
|
+
pattern: '^0x[a-fA-F0-9]{40}$',
|
|
150
|
+
},
|
|
151
|
+
percentAllocation: { type: 'number' },
|
|
152
|
+
},
|
|
153
|
+
required: ['address', 'percentAllocation'],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
OVMOwnerAddress: {
|
|
157
|
+
type: 'string',
|
|
158
|
+
pattern: '^0x[a-fA-F0-9]{40}$',
|
|
159
|
+
},
|
|
160
|
+
splitOwnerAddress: {
|
|
161
|
+
type: 'string',
|
|
162
|
+
pattern: '^0x[a-fA-F0-9]{40}$',
|
|
163
|
+
default: zeroAddress,
|
|
164
|
+
},
|
|
165
|
+
principalThreshold: {
|
|
166
|
+
type: 'number',
|
|
167
|
+
minimum: 16,
|
|
168
|
+
default: PRINCIPAL_THRESHOLD,
|
|
169
|
+
},
|
|
170
|
+
distributorFeePercent: {
|
|
171
|
+
type: 'number',
|
|
172
|
+
minimum: 0,
|
|
173
|
+
maximum: 10,
|
|
174
|
+
default: 0,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
export const ovmRewardsSplitPayloadSchema = {
|
|
178
|
+
type: 'object',
|
|
179
|
+
properties: Object.assign(Object.assign({}, ovmBaseSplitPayload), { principalRecipient: {
|
|
180
|
+
type: 'string',
|
|
181
|
+
pattern: '^0x[a-fA-F0-9]{40}$',
|
|
182
|
+
} }),
|
|
183
|
+
validateOVMRewardsSplitRecipients: true,
|
|
184
|
+
required: ['rewardSplitRecipients', 'OVMOwnerAddress', 'principalRecipient'],
|
|
185
|
+
};
|
|
186
|
+
export const ovmTotalSplitPayloadSchema = {
|
|
187
|
+
type: 'object',
|
|
188
|
+
properties: Object.assign(Object.assign({}, ovmBaseSplitPayload), { principalSplitRecipients: {
|
|
189
|
+
type: 'array',
|
|
190
|
+
items: {
|
|
191
|
+
type: 'object',
|
|
192
|
+
properties: {
|
|
193
|
+
address: {
|
|
194
|
+
type: 'string',
|
|
195
|
+
pattern: '^0x[a-fA-F0-9]{40}$',
|
|
196
|
+
},
|
|
197
|
+
percentAllocation: { type: 'number' },
|
|
198
|
+
},
|
|
199
|
+
required: ['address', 'percentAllocation'],
|
|
200
|
+
},
|
|
201
|
+
} }),
|
|
202
|
+
validateOVMRewardsSplitRecipients: true,
|
|
203
|
+
validateOVMTotalSplitRecipients: true,
|
|
204
|
+
required: [
|
|
205
|
+
'rewardSplitRecipients',
|
|
206
|
+
'principalSplitRecipients',
|
|
207
|
+
'OVMOwnerAddress',
|
|
208
|
+
],
|
|
209
|
+
};
|
|
@@ -7,19 +7,38 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { Contract, Interface, parseEther, ZeroAddress } from 'ethers';
|
|
10
|
+
import { BrowserProvider, Contract, Interface, parseEther, ZeroAddress, } from 'ethers';
|
|
11
11
|
import { OWRContract, OWRFactoryContract } from '../abi/OWR';
|
|
12
|
+
import { OVMFactoryContract } from '../abi/OVMFactory';
|
|
12
13
|
import { splitMainEthereumAbi } from '../abi/SplitMain';
|
|
13
14
|
import { MultiCallContract } from '../abi/Multicall';
|
|
14
|
-
import { CHAIN_CONFIGURATION } from '../constants';
|
|
15
|
+
import { CHAIN_CONFIGURATION, CHAIN_PUBLIC_RPC_URL } from '../constants';
|
|
16
|
+
import { SplitsClient } from '@0xsplits/splits-sdk';
|
|
17
|
+
import { createPublicClient, createWalletClient, custom, http } from 'viem';
|
|
18
|
+
import { hoodi, mainnet } from 'viem/chains';
|
|
19
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
15
20
|
const splitMainContractInterface = new Interface(splitMainEthereumAbi);
|
|
16
21
|
const owrFactoryContractInterface = new Interface(OWRFactoryContract.abi);
|
|
17
|
-
|
|
22
|
+
const ovmFactoryContractInterface = new Interface(OVMFactoryContract.abi);
|
|
23
|
+
// Helper function to extract common recipient formatting logic
|
|
24
|
+
const formatRecipientsCommon = (recipients) => {
|
|
25
|
+
const copiedRecipients = [...recipients];
|
|
26
|
+
// Handle both SplitRecipient and SplitV2Recipient types
|
|
27
|
+
const getAddress = (item) => item.account || item.address;
|
|
28
|
+
const getPercentAllocation = (item) => item.percentAllocation;
|
|
18
29
|
// Has to be sorted when passed
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
copiedRecipients.sort((a, b) => getAddress(a).localeCompare(getAddress(b)));
|
|
31
|
+
return {
|
|
32
|
+
sortedRecipients: copiedRecipients,
|
|
33
|
+
getAddress,
|
|
34
|
+
getPercentAllocation,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export const formatSplitRecipients = (recipients) => {
|
|
38
|
+
const { sortedRecipients, getAddress, getPercentAllocation } = formatRecipientsCommon(recipients);
|
|
39
|
+
const accounts = sortedRecipients.map(item => getAddress(item));
|
|
40
|
+
const percentAllocations = sortedRecipients.map(recipient => {
|
|
41
|
+
const splitTostring = (getPercentAllocation(recipient) * 1e4).toFixed(0);
|
|
23
42
|
return parseInt(splitTostring);
|
|
24
43
|
});
|
|
25
44
|
return { accounts, percentAllocations };
|
|
@@ -27,7 +46,7 @@ export const formatSplitRecipients = (recipients) => {
|
|
|
27
46
|
export const predictSplitterAddress = ({ signer, accounts, percentAllocations, chainId, distributorFee, controllerAddress, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
47
|
var _a, _b, _c;
|
|
29
48
|
try {
|
|
30
|
-
const splitMainContractInstance = new Contract(
|
|
49
|
+
const splitMainContractInstance = new Contract(getChainConfig(chainId).SPLITMAIN_ADDRESS.address, splitMainEthereumAbi, signer);
|
|
31
50
|
let predictedSplitterAddress;
|
|
32
51
|
if (controllerAddress === ZeroAddress) {
|
|
33
52
|
try {
|
|
@@ -128,7 +147,7 @@ export const handleDeployOWRAndSplitter = ({ signer, isSplitterDeployed, predict
|
|
|
128
147
|
const createOWRContract = ({ owrArgs, signer, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
129
148
|
var _g, _h, _j, _k, _l;
|
|
130
149
|
try {
|
|
131
|
-
const OWRFactoryInstance = new Contract(
|
|
150
|
+
const OWRFactoryInstance = new Contract(getChainConfig(chainId).OWR_FACTORY_ADDRESS.address, OWRFactoryContract.abi, signer);
|
|
132
151
|
let tx;
|
|
133
152
|
try {
|
|
134
153
|
tx = yield OWRFactoryInstance.createOWRecipient(owrArgs.recoveryAddress, owrArgs.principalRecipient, owrArgs.predictedSplitterAddress, parseEther(owrArgs.amountOfPrincipalStake.toString()));
|
|
@@ -172,7 +191,7 @@ const createOWRContract = ({ owrArgs, signer, chainId, }) => __awaiter(void 0, v
|
|
|
172
191
|
export const deploySplitterContract = ({ signer, accounts, percentAllocations, chainId, distributorFee, controllerAddress, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
173
192
|
var _m, _o, _p, _q, _r;
|
|
174
193
|
try {
|
|
175
|
-
const splitMainContractInstance = new Contract(
|
|
194
|
+
const splitMainContractInstance = new Contract(getChainConfig(chainId).SPLITMAIN_ADDRESS.address, splitMainEthereumAbi, signer);
|
|
176
195
|
let tx;
|
|
177
196
|
try {
|
|
178
197
|
tx = yield splitMainContractInstance.createSplit(accounts, percentAllocations, distributorFee, controllerAddress);
|
|
@@ -219,13 +238,13 @@ export const deploySplitterAndOWRContracts = ({ owrArgs, splitterArgs, signer, c
|
|
|
219
238
|
const splitTxData = encodeCreateSplitTxData(splitterArgs.accounts, splitterArgs.percentAllocations, splitterArgs.distributorFee, splitterArgs.controllerAddress);
|
|
220
239
|
const owrTxData = encodeCreateOWRecipientTxData(owrArgs.recoveryAddress, owrArgs.principalRecipient, owrArgs.predictedSplitterAddress, owrArgs.amountOfPrincipalStake);
|
|
221
240
|
executeCalls.push({
|
|
222
|
-
target:
|
|
241
|
+
target: getChainConfig(chainId).SPLITMAIN_ADDRESS.address,
|
|
223
242
|
callData: splitTxData,
|
|
224
243
|
}, {
|
|
225
|
-
target:
|
|
244
|
+
target: getChainConfig(chainId).OWR_FACTORY_ADDRESS.address,
|
|
226
245
|
callData: owrTxData,
|
|
227
246
|
});
|
|
228
|
-
const multicallAddess =
|
|
247
|
+
const multicallAddess = getChainConfig(chainId).MULTICALL_ADDRESS.address;
|
|
229
248
|
const executeMultiCalls = yield multicall(executeCalls, signer, multicallAddess);
|
|
230
249
|
const splitAddressData = (_s = executeMultiCalls === null || executeMultiCalls === void 0 ? void 0 : executeMultiCalls.logs[0]) === null || _s === void 0 ? void 0 : _s.topics[1];
|
|
231
250
|
const formattedSplitterAddress = '0x' + (splitAddressData === null || splitAddressData === void 0 ? void 0 : splitAddressData.slice(26, 66));
|
|
@@ -315,3 +334,184 @@ const encodeCreateOWRecipientTxData = (recoveryAddress, principalRecipient, rewa
|
|
|
315
334
|
parseEther(amountOfPrincipalStake.toString()),
|
|
316
335
|
]);
|
|
317
336
|
};
|
|
337
|
+
// OVM and SplitV2 Helper Functions
|
|
338
|
+
// Helper function to format recipients specifically for SplitV2 (returns SplitV2Recipient[])
|
|
339
|
+
export const formatRecipientsForSplitV2 = (splitRecipients) => {
|
|
340
|
+
const { sortedRecipients, getAddress, getPercentAllocation } = formatRecipientsCommon(splitRecipients);
|
|
341
|
+
return sortedRecipients
|
|
342
|
+
.filter(item => getAddress(item) !== '')
|
|
343
|
+
.map(item => ({
|
|
344
|
+
address: getAddress(item),
|
|
345
|
+
percentAllocation: parseFloat(getPercentAllocation(item).toString()),
|
|
346
|
+
}));
|
|
347
|
+
};
|
|
348
|
+
export const createSplitsClient = (signer, chainId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
349
|
+
const chain = chainId === 1 ? mainnet : hoodi;
|
|
350
|
+
const client = createPublicClient({
|
|
351
|
+
chain,
|
|
352
|
+
transport: http(CHAIN_PUBLIC_RPC_URL[chainId]),
|
|
353
|
+
});
|
|
354
|
+
// convert signer to walletClient
|
|
355
|
+
if (typeof window !== 'undefined' &&
|
|
356
|
+
signer.provider instanceof BrowserProvider) {
|
|
357
|
+
// For browser environment
|
|
358
|
+
const address = yield signer.getAddress();
|
|
359
|
+
const viemWalletClient = createWalletClient({
|
|
360
|
+
account: address,
|
|
361
|
+
chain,
|
|
362
|
+
transport: custom(window.ethereum),
|
|
363
|
+
});
|
|
364
|
+
return new SplitsClient({
|
|
365
|
+
publicClient: client,
|
|
366
|
+
walletClient: viemWalletClient,
|
|
367
|
+
chainId,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
// For non-browser environment, extract private key from signer
|
|
372
|
+
const signerPrivateKey = signer === null || signer === void 0 ? void 0 : signer.privateKey;
|
|
373
|
+
if (!signerPrivateKey) {
|
|
374
|
+
throw new Error('Signer private key not available');
|
|
375
|
+
}
|
|
376
|
+
const privateKey = signerPrivateKey.startsWith('0x')
|
|
377
|
+
? signerPrivateKey
|
|
378
|
+
: `0x${signerPrivateKey}`;
|
|
379
|
+
const scriptAccount = privateKeyToAccount(privateKey);
|
|
380
|
+
const account = scriptAccount;
|
|
381
|
+
const viemWalletClient = createWalletClient({
|
|
382
|
+
account,
|
|
383
|
+
chain,
|
|
384
|
+
transport: http(CHAIN_PUBLIC_RPC_URL[chainId]),
|
|
385
|
+
});
|
|
386
|
+
return new SplitsClient({
|
|
387
|
+
publicClient: client,
|
|
388
|
+
walletClient: viemWalletClient,
|
|
389
|
+
chainId,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
export const predictSplitV2Address = ({ splitOwnerAddress, recipients, distributorFeePercent, salt, signer, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
394
|
+
var _z;
|
|
395
|
+
try {
|
|
396
|
+
const splitsClient = yield createSplitsClient(signer, chainId);
|
|
397
|
+
const response = yield splitsClient.splitV2.predictDeterministicAddress({
|
|
398
|
+
ownerAddress: splitOwnerAddress,
|
|
399
|
+
recipients,
|
|
400
|
+
distributorFeePercent,
|
|
401
|
+
salt,
|
|
402
|
+
});
|
|
403
|
+
return response.splitAddress;
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
throw new Error(`Failed to predict SplitV2 address: ${(_z = error.message) !== null && _z !== void 0 ? _z : 'SplitV2 SDK call failed'}`);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
export const isSplitV2Deployed = ({ splitOwnerAddress, recipients, distributorFeePercent, salt, signer, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
410
|
+
try {
|
|
411
|
+
const splitsClient = yield createSplitsClient(signer, chainId);
|
|
412
|
+
const response = yield splitsClient.splitV2.isDeployed({
|
|
413
|
+
ownerAddress: splitOwnerAddress,
|
|
414
|
+
recipients,
|
|
415
|
+
distributorFeePercent,
|
|
416
|
+
salt,
|
|
417
|
+
});
|
|
418
|
+
return response.deployed;
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
// If the check fails, assume it's not deployed
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
export const deployOVMContract = ({ OVMOwnerAddress, principalRecipient, rewardRecipient, principalThreshold, signer, chainId, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
426
|
+
var _0, _1, _2;
|
|
427
|
+
try {
|
|
428
|
+
const chainConfig = getChainConfig(chainId);
|
|
429
|
+
if (!((_0 = chainConfig === null || chainConfig === void 0 ? void 0 : chainConfig.OVM_FACTORY_ADDRESS) === null || _0 === void 0 ? void 0 : _0.address)) {
|
|
430
|
+
throw new Error(`OVM Factory not configured for chain ${chainId}`);
|
|
431
|
+
}
|
|
432
|
+
const ovmFactoryContract = new Contract(chainConfig.OVM_FACTORY_ADDRESS.address, OVMFactoryContract.abi, signer);
|
|
433
|
+
const tx = yield ovmFactoryContract.createObolValidatorManager(OVMOwnerAddress, principalRecipient, rewardRecipient, principalThreshold);
|
|
434
|
+
const receipt = yield tx.wait();
|
|
435
|
+
// Extract OVM address from logs
|
|
436
|
+
const ovmAddressLog = (_1 = receipt === null || receipt === void 0 ? void 0 : receipt.logs[1]) === null || _1 === void 0 ? void 0 : _1.topics[1];
|
|
437
|
+
if (!ovmAddressLog) {
|
|
438
|
+
throw new Error('Failed to extract OVM address from transaction logs');
|
|
439
|
+
}
|
|
440
|
+
const ovmAddress = '0x' + ovmAddressLog.slice(26, 66);
|
|
441
|
+
return ovmAddress;
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
throw new Error(`Failed to deploy OVM contract: ${(_2 = error.message) !== null && _2 !== void 0 ? _2 : 'OVM deployment failed'}`);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
export const deployOVMAndSplitV2 = ({ ovmArgs, rewardRecipients, isRewardsSplitterDeployed, distributorFeePercent, salt, signer, chainId, principalSplitRecipients, isPrincipalSplitDeployed, splitOwnerAddress, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
448
|
+
var _3, _4, _5, _6, _7;
|
|
449
|
+
try {
|
|
450
|
+
const chainConfig = getChainConfig(chainId);
|
|
451
|
+
if (!((_3 = chainConfig === null || chainConfig === void 0 ? void 0 : chainConfig.OVM_FACTORY_ADDRESS) === null || _3 === void 0 ? void 0 : _3.address)) {
|
|
452
|
+
throw new Error(`OVM Factory not configured for chain ${chainId}`);
|
|
453
|
+
}
|
|
454
|
+
const splitsClient = yield createSplitsClient(signer, chainId);
|
|
455
|
+
const executeCalls = [];
|
|
456
|
+
if (rewardRecipients && !isRewardsSplitterDeployed) {
|
|
457
|
+
// Create rewards split call data
|
|
458
|
+
const rewardsSplitTxData = yield splitsClient.splitV2.callData.createSplit({
|
|
459
|
+
recipients: rewardRecipients,
|
|
460
|
+
distributorFeePercent,
|
|
461
|
+
ownerAddress: splitOwnerAddress,
|
|
462
|
+
salt,
|
|
463
|
+
});
|
|
464
|
+
executeCalls.push(rewardsSplitTxData);
|
|
465
|
+
}
|
|
466
|
+
// Create principal split call data if needed (for total split scenario)
|
|
467
|
+
if (principalSplitRecipients && !isPrincipalSplitDeployed) {
|
|
468
|
+
const principalSplitTxData = yield splitsClient.splitV2.callData.createSplit({
|
|
469
|
+
recipients: principalSplitRecipients,
|
|
470
|
+
distributorFeePercent,
|
|
471
|
+
salt,
|
|
472
|
+
ownerAddress: splitOwnerAddress,
|
|
473
|
+
});
|
|
474
|
+
executeCalls.push(principalSplitTxData);
|
|
475
|
+
}
|
|
476
|
+
// Create OVM call data
|
|
477
|
+
const ovmTxData = encodeCreateOVMTxData(ovmArgs.OVMOwnerAddress, ovmArgs.principalRecipient, ovmArgs.rewardRecipient, ovmArgs.principalThreshold);
|
|
478
|
+
executeCalls.push({
|
|
479
|
+
address: chainConfig.OVM_FACTORY_ADDRESS.address,
|
|
480
|
+
data: ovmTxData,
|
|
481
|
+
});
|
|
482
|
+
// Execute multicall
|
|
483
|
+
const executeMultiCalls = yield splitsClient.splitV2.multicall({
|
|
484
|
+
calls: executeCalls,
|
|
485
|
+
});
|
|
486
|
+
// Extract addresses from events
|
|
487
|
+
let ovmAddress;
|
|
488
|
+
const eventCount = executeMultiCalls === null || executeMultiCalls === void 0 ? void 0 : executeMultiCalls.events.length;
|
|
489
|
+
if (eventCount === 2) {
|
|
490
|
+
ovmAddress = (_4 = executeMultiCalls === null || executeMultiCalls === void 0 ? void 0 : executeMultiCalls.events[0]) === null || _4 === void 0 ? void 0 : _4.address;
|
|
491
|
+
}
|
|
492
|
+
else if (eventCount === 5) {
|
|
493
|
+
ovmAddress = (_5 = executeMultiCalls === null || executeMultiCalls === void 0 ? void 0 : executeMultiCalls.events[3]) === null || _5 === void 0 ? void 0 : _5.address;
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
ovmAddress = (_6 = executeMultiCalls === null || executeMultiCalls === void 0 ? void 0 : executeMultiCalls.events[6]) === null || _6 === void 0 ? void 0 : _6.address;
|
|
497
|
+
}
|
|
498
|
+
if (!ovmAddress) {
|
|
499
|
+
throw new Error('Failed to extract contract addresses from multicall events');
|
|
500
|
+
}
|
|
501
|
+
return ovmAddress;
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
throw new Error(`Failed to deploy OVM and SplitV2: ${(_7 = error.message) !== null && _7 !== void 0 ? _7 : 'Deployment failed'}`);
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
const encodeCreateOVMTxData = (OVMOwnerAddress, principalRecipient, rewardRecipient, principalThreshold) => {
|
|
508
|
+
return ovmFactoryContractInterface.encodeFunctionData('createObolValidatorManager', [OVMOwnerAddress, principalRecipient, rewardRecipient, principalThreshold]);
|
|
509
|
+
};
|
|
510
|
+
// Helper function to safely get chain configuration
|
|
511
|
+
const getChainConfig = (chainId) => {
|
|
512
|
+
const config = CHAIN_CONFIGURATION[chainId];
|
|
513
|
+
if (!config) {
|
|
514
|
+
throw new Error(`Chain configuration not found for chain ID ${chainId}`);
|
|
515
|
+
}
|
|
516
|
+
return config;
|
|
517
|
+
};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { formatRecipientsForSplitV2, predictSplitV2Address, isSplitV2Deployed, deployOVMContract, deployOVMAndSplitV2, } from './splitHelpers';
|
|
11
|
+
import { CHAIN_CONFIGURATION, SPLITS_V2_SALT, DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT, isChainSupportedForSplitters, } from '../constants';
|
|
12
|
+
import { ovmRewardsSplitPayloadSchema, ovmTotalSplitPayloadSchema, } from '../schema';
|
|
13
|
+
import { validatePayload } from '../ajv';
|
|
14
|
+
import { isContractAvailable } from '../utils';
|
|
15
|
+
/**
|
|
16
|
+
* ObolSplits can be used for creating and managing Obol splits.
|
|
17
|
+
* @class
|
|
18
|
+
* @internal Access it through Client.splits.
|
|
19
|
+
* @example
|
|
20
|
+
* const obolClient = new Client(config);
|
|
21
|
+
* await obolClient.splits.createValidatorManagerAndRewardsSplit(OVMRewardsSplitPayload);
|
|
22
|
+
*/
|
|
23
|
+
export class ObolSplits {
|
|
24
|
+
constructor(signer, chainId, provider) {
|
|
25
|
+
this.signer = signer;
|
|
26
|
+
this.chainId = chainId;
|
|
27
|
+
this.provider = provider;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Creates an Obol OVM and Pull split configuration for rewards-only scenario.
|
|
31
|
+
*
|
|
32
|
+
* This method deploys OVM and SplitV2 contracts for managing validator rewards only.
|
|
33
|
+
* Principal is handled by a single address, while rewards are split among recipients.
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* **⚠️ Important:** If you're storing the private key in an `.env` file, ensure it is securely managed
|
|
37
|
+
* and not pushed to version control.
|
|
38
|
+
*
|
|
39
|
+
* @param {OVMRewardsSplitPayload} payload - Data needed to deploy OVM and SplitV2
|
|
40
|
+
* @returns {Promise<ClusterValidator>} OVM address as withdrawal address and splitter as fee recipient
|
|
41
|
+
* @throws Will throw an error if the splitter configuration is not supported or deployment fails
|
|
42
|
+
*
|
|
43
|
+
* An example of how to use createValidatorManagerAndRewardsSplit:
|
|
44
|
+
* [createValidatorManagerAndRewardsSplit](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L333)
|
|
45
|
+
*/
|
|
46
|
+
createValidatorManagerAndRewardsSplit(payload) {
|
|
47
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
const salt = SPLITS_V2_SALT;
|
|
49
|
+
if (!this.signer) {
|
|
50
|
+
throw new Error('Signer is required in createValidatorManagerAndRewardsSplit');
|
|
51
|
+
}
|
|
52
|
+
const validatedPayload = validatePayload(payload, ovmRewardsSplitPayloadSchema);
|
|
53
|
+
if (!isChainSupportedForSplitters(this.chainId)) {
|
|
54
|
+
throw new Error(`Splitter configuration is not supported on ${this.chainId} chain`);
|
|
55
|
+
}
|
|
56
|
+
const chainConfig = CHAIN_CONFIGURATION[this.chainId];
|
|
57
|
+
if (!(chainConfig === null || chainConfig === void 0 ? void 0 : chainConfig.OVM_FACTORY_ADDRESS)) {
|
|
58
|
+
throw new Error(`OVM contract factory is not configured for chain ${this.chainId}`);
|
|
59
|
+
}
|
|
60
|
+
if (!this.provider) {
|
|
61
|
+
throw new Error('Provider is required to check OVM factory contract availability');
|
|
62
|
+
}
|
|
63
|
+
const ovmFactoryConfig = chainConfig.OVM_FACTORY_ADDRESS;
|
|
64
|
+
if (!(ovmFactoryConfig === null || ovmFactoryConfig === void 0 ? void 0 : ovmFactoryConfig.address) || !(ovmFactoryConfig === null || ovmFactoryConfig === void 0 ? void 0 : ovmFactoryConfig.bytecode)) {
|
|
65
|
+
throw new Error(`OVM factory contract configuration is incomplete for chain ${this.chainId}`);
|
|
66
|
+
}
|
|
67
|
+
const checkOVMFactoryAddress = yield isContractAvailable(ovmFactoryConfig.address, this.provider, ovmFactoryConfig.bytecode);
|
|
68
|
+
if (!checkOVMFactoryAddress) {
|
|
69
|
+
throw new Error(`OVM factory contract is not deployed or available on chain ${this.chainId}`);
|
|
70
|
+
}
|
|
71
|
+
const retroActiveFundingRecipient = {
|
|
72
|
+
address: chainConfig.RETROACTIVE_FUNDING_ADDRESS.address,
|
|
73
|
+
percentAllocation: DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
|
|
74
|
+
};
|
|
75
|
+
const copiedRewardsSplitRecipients = [
|
|
76
|
+
...validatedPayload.rewardSplitRecipients,
|
|
77
|
+
];
|
|
78
|
+
copiedRewardsSplitRecipients.push(retroActiveFundingRecipient);
|
|
79
|
+
// Format recipients for SplitV2
|
|
80
|
+
const rewardRecipients = formatRecipientsForSplitV2(copiedRewardsSplitRecipients);
|
|
81
|
+
const predictedSplitAddress = yield predictSplitV2Address({
|
|
82
|
+
splitOwnerAddress: validatedPayload.splitOwnerAddress,
|
|
83
|
+
recipients: rewardRecipients,
|
|
84
|
+
distributorFeePercent: validatedPayload.distributorFeePercent,
|
|
85
|
+
salt,
|
|
86
|
+
signer: this.signer,
|
|
87
|
+
chainId: this.chainId,
|
|
88
|
+
});
|
|
89
|
+
const isRewardSplitterDeployed = yield isSplitV2Deployed({
|
|
90
|
+
splitOwnerAddress: validatedPayload.splitOwnerAddress,
|
|
91
|
+
recipients: rewardRecipients,
|
|
92
|
+
distributorFeePercent: validatedPayload.distributorFeePercent,
|
|
93
|
+
salt,
|
|
94
|
+
signer: this.signer,
|
|
95
|
+
chainId: this.chainId,
|
|
96
|
+
});
|
|
97
|
+
if (isRewardSplitterDeployed) {
|
|
98
|
+
const ovmAddress = yield deployOVMContract({
|
|
99
|
+
OVMOwnerAddress: validatedPayload.OVMOwnerAddress,
|
|
100
|
+
principalRecipient: validatedPayload.principalRecipient,
|
|
101
|
+
rewardRecipient: predictedSplitAddress,
|
|
102
|
+
principalThreshold: validatedPayload.principalThreshold,
|
|
103
|
+
signer: this.signer,
|
|
104
|
+
chainId: this.chainId,
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
withdrawal_address: ovmAddress,
|
|
108
|
+
fee_recipient_address: predictedSplitAddress,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const ovmAddress = yield deployOVMAndSplitV2({
|
|
113
|
+
ovmArgs: {
|
|
114
|
+
OVMOwnerAddress: validatedPayload.OVMOwnerAddress,
|
|
115
|
+
rewardRecipient: predictedSplitAddress,
|
|
116
|
+
principalRecipient: validatedPayload.principalRecipient,
|
|
117
|
+
principalThreshold: validatedPayload.principalThreshold,
|
|
118
|
+
},
|
|
119
|
+
rewardRecipients,
|
|
120
|
+
isRewardsSplitterDeployed: isRewardSplitterDeployed,
|
|
121
|
+
distributorFeePercent: validatedPayload.distributorFeePercent,
|
|
122
|
+
salt,
|
|
123
|
+
signer: this.signer,
|
|
124
|
+
chainId: this.chainId,
|
|
125
|
+
splitOwnerAddress: validatedPayload.splitOwnerAddress,
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
withdrawal_address: ovmAddress,
|
|
129
|
+
fee_recipient_address: predictedSplitAddress,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Creates an Obol OVM and Total split configuration for total split scenario.
|
|
136
|
+
*
|
|
137
|
+
* This method deploys OVM and SplitV2 contracts for managing both validator rewards and principal.
|
|
138
|
+
* Both rewards and principal are split among recipients, with rewards including RAF recipient.
|
|
139
|
+
*
|
|
140
|
+
* @remarks
|
|
141
|
+
* **⚠️ Important:** If you're storing the private key in an `.env` file, ensure it is securely managed
|
|
142
|
+
* and not pushed to version control.
|
|
143
|
+
*
|
|
144
|
+
* @param {OVMTotalSplitPayload} payload - Data needed to deploy OVM and SplitV2
|
|
145
|
+
* @returns {Promise<ClusterValidator>} OVM address as withdrawal address and splitter as fee recipient
|
|
146
|
+
* @throws Will throw an error if the splitter configuration is not supported or deployment fails
|
|
147
|
+
*
|
|
148
|
+
* An example of how to use createValidatorManagerAndTotalSplit:
|
|
149
|
+
* [createValidatorManagerAndTotalSplit](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#340)
|
|
150
|
+
*/
|
|
151
|
+
createValidatorManagerAndTotalSplit(payload) {
|
|
152
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
153
|
+
const salt = SPLITS_V2_SALT;
|
|
154
|
+
if (!this.signer) {
|
|
155
|
+
throw new Error('Signer is required in createValidatorManagerAndTotalSplit');
|
|
156
|
+
}
|
|
157
|
+
const validatedPayload = validatePayload(payload, ovmTotalSplitPayloadSchema);
|
|
158
|
+
if (!isChainSupportedForSplitters(this.chainId)) {
|
|
159
|
+
throw new Error(`Splitter configuration is not supported on ${this.chainId} chain`);
|
|
160
|
+
}
|
|
161
|
+
const chainConfig = CHAIN_CONFIGURATION[this.chainId];
|
|
162
|
+
if (!(chainConfig === null || chainConfig === void 0 ? void 0 : chainConfig.OVM_FACTORY_ADDRESS)) {
|
|
163
|
+
throw new Error(`OVM contract factory is not configured for chain ${this.chainId}`);
|
|
164
|
+
}
|
|
165
|
+
if (!this.provider) {
|
|
166
|
+
throw new Error('Provider is required to check OVM factory contract availability');
|
|
167
|
+
}
|
|
168
|
+
const ovmFactoryConfig = chainConfig.OVM_FACTORY_ADDRESS;
|
|
169
|
+
if (!(ovmFactoryConfig === null || ovmFactoryConfig === void 0 ? void 0 : ovmFactoryConfig.address) || !(ovmFactoryConfig === null || ovmFactoryConfig === void 0 ? void 0 : ovmFactoryConfig.bytecode)) {
|
|
170
|
+
throw new Error(`OVM factory contract configuration is incomplete for chain ${this.chainId}`);
|
|
171
|
+
}
|
|
172
|
+
const checkOVMFactoryAddress = yield isContractAvailable(ovmFactoryConfig.address, this.provider, ovmFactoryConfig.bytecode);
|
|
173
|
+
if (!checkOVMFactoryAddress) {
|
|
174
|
+
throw new Error(`OVM factory contract is not deployed or available on chain ${this.chainId}`);
|
|
175
|
+
}
|
|
176
|
+
const retroActiveFundingRecipient = {
|
|
177
|
+
address: chainConfig.RETROACTIVE_FUNDING_ADDRESS.address,
|
|
178
|
+
percentAllocation: DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
|
|
179
|
+
};
|
|
180
|
+
const copiedRewardsSplitRecipients = [
|
|
181
|
+
...validatedPayload.rewardSplitRecipients,
|
|
182
|
+
];
|
|
183
|
+
copiedRewardsSplitRecipients.push(retroActiveFundingRecipient);
|
|
184
|
+
const rewardsRecipients = formatRecipientsForSplitV2(copiedRewardsSplitRecipients);
|
|
185
|
+
const principalSplitRecipients = formatRecipientsForSplitV2(validatedPayload.principalSplitRecipients);
|
|
186
|
+
const predictedRewardsSplitAddress = yield predictSplitV2Address({
|
|
187
|
+
splitOwnerAddress: validatedPayload.splitOwnerAddress,
|
|
188
|
+
recipients: rewardsRecipients,
|
|
189
|
+
distributorFeePercent: validatedPayload.distributorFeePercent,
|
|
190
|
+
salt,
|
|
191
|
+
signer: this.signer,
|
|
192
|
+
chainId: this.chainId,
|
|
193
|
+
});
|
|
194
|
+
const predictedPrincipalSplitAddress = yield predictSplitV2Address({
|
|
195
|
+
splitOwnerAddress: validatedPayload.splitOwnerAddress,
|
|
196
|
+
recipients: principalSplitRecipients,
|
|
197
|
+
distributorFeePercent: validatedPayload.distributorFeePercent,
|
|
198
|
+
salt,
|
|
199
|
+
signer: this.signer,
|
|
200
|
+
chainId: this.chainId,
|
|
201
|
+
});
|
|
202
|
+
const isRewardsSplitterDeployed = yield isSplitV2Deployed({
|
|
203
|
+
splitOwnerAddress: validatedPayload.splitOwnerAddress,
|
|
204
|
+
recipients: rewardsRecipients,
|
|
205
|
+
distributorFeePercent: validatedPayload.distributorFeePercent,
|
|
206
|
+
salt,
|
|
207
|
+
signer: this.signer,
|
|
208
|
+
chainId: this.chainId,
|
|
209
|
+
});
|
|
210
|
+
const isPrincipalSplitterDeployed = yield isSplitV2Deployed({
|
|
211
|
+
splitOwnerAddress: validatedPayload.splitOwnerAddress,
|
|
212
|
+
recipients: principalSplitRecipients,
|
|
213
|
+
distributorFeePercent: validatedPayload.distributorFeePercent,
|
|
214
|
+
salt,
|
|
215
|
+
signer: this.signer,
|
|
216
|
+
chainId: this.chainId,
|
|
217
|
+
});
|
|
218
|
+
if (isRewardsSplitterDeployed && isPrincipalSplitterDeployed) {
|
|
219
|
+
const ovmAddress = yield deployOVMContract({
|
|
220
|
+
OVMOwnerAddress: validatedPayload.OVMOwnerAddress,
|
|
221
|
+
principalRecipient: predictedPrincipalSplitAddress,
|
|
222
|
+
rewardRecipient: predictedRewardsSplitAddress,
|
|
223
|
+
principalThreshold: validatedPayload.principalThreshold,
|
|
224
|
+
signer: this.signer,
|
|
225
|
+
chainId: this.chainId,
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
withdrawal_address: ovmAddress,
|
|
229
|
+
fee_recipient_address: predictedRewardsSplitAddress,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
// Use multicall to deploy any contracts that aren't deployed
|
|
234
|
+
const ovmAddress = yield deployOVMAndSplitV2({
|
|
235
|
+
ovmArgs: {
|
|
236
|
+
OVMOwnerAddress: validatedPayload.OVMOwnerAddress,
|
|
237
|
+
rewardRecipient: predictedRewardsSplitAddress,
|
|
238
|
+
principalRecipient: predictedPrincipalSplitAddress,
|
|
239
|
+
principalThreshold: validatedPayload.principalThreshold,
|
|
240
|
+
},
|
|
241
|
+
rewardRecipients: rewardsRecipients,
|
|
242
|
+
distributorFeePercent: validatedPayload.distributorFeePercent,
|
|
243
|
+
salt,
|
|
244
|
+
signer: this.signer,
|
|
245
|
+
chainId: this.chainId,
|
|
246
|
+
principalSplitRecipients,
|
|
247
|
+
isPrincipalSplitDeployed: isPrincipalSplitterDeployed,
|
|
248
|
+
splitOwnerAddress: validatedPayload.splitOwnerAddress,
|
|
249
|
+
});
|
|
250
|
+
return {
|
|
251
|
+
withdrawal_address: ovmAddress,
|
|
252
|
+
fee_recipient_address: predictedRewardsSplitAddress,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
package/dist/esm/src/types.js
CHANGED