@obolnetwork/obol-sdk 2.4.5 → 2.5.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/README.md +1 -1
- package/dist/cjs/package.json +4 -2
- package/dist/cjs/src/ajv.js +67 -41
- package/dist/cjs/src/constants.js +1 -1
- package/dist/cjs/src/incentiveHelpers.js +2 -4
- package/dist/cjs/src/incentives.js +1 -1
- package/dist/cjs/src/index.js +25 -27
- package/dist/cjs/src/schema.js +50 -23
- package/dist/cjs/src/services.js +3 -2
- package/dist/cjs/src/utils.js +4 -4
- package/dist/cjs/src/verification/common.js +12 -9
- package/dist/cjs/src/verification/signature-validator.js +6 -5
- package/dist/cjs/src/verification/v1.8.0.js +18 -0
- package/dist/cjs/test/fixtures.js +125 -17
- package/dist/cjs/test/incentives.test.js +2 -2
- package/dist/cjs/test/methods.test.js +47 -42
- package/dist/esm/package.json +4 -2
- package/dist/esm/src/ajv.js +66 -40
- package/dist/esm/src/constants.js +1 -1
- package/dist/esm/src/incentiveHelpers.js +2 -4
- package/dist/esm/src/incentives.js +1 -1
- package/dist/esm/src/index.js +26 -28
- package/dist/esm/src/schema.js +50 -23
- package/dist/esm/src/services.js +3 -2
- package/dist/esm/src/utils.js +4 -4
- package/dist/esm/src/verification/common.js +12 -9
- package/dist/esm/src/verification/signature-validator.js +6 -5
- package/dist/esm/src/verification/v1.8.0.js +18 -0
- package/dist/esm/test/fixtures.js +124 -16
- package/dist/esm/test/incentives.test.js +2 -2
- package/dist/esm/test/methods.test.js +48 -43
- package/dist/types/src/ajv.d.ts +3 -2
- package/dist/types/src/constants.d.ts +1 -1
- package/dist/types/src/incentiveHelpers.d.ts +1 -1
- package/dist/types/src/incentives.d.ts +2 -2
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/schema.d.ts +40 -8
- package/dist/types/src/services.d.ts +3 -2
- package/dist/types/src/types.d.ts +16 -6
- package/dist/types/src/utils.d.ts +1 -1
- package/dist/types/src/verification/common.d.ts +2 -2
- package/dist/types/src/verification/signature-validator.d.ts +5 -2
- package/dist/types/test/fixtures.d.ts +78 -11
- package/package.json +4 -2
- package/src/ajv.ts +91 -52
- package/src/constants.ts +1 -1
- package/src/incentiveHelpers.ts +1 -5
- package/src/incentives.ts +3 -4
- package/src/index.ts +37 -35
- package/src/schema.ts +45 -22
- package/src/services.ts +4 -2
- package/src/types.ts +20 -5
- package/src/utils.ts +7 -4
- package/src/verification/common.ts +13 -1
- package/src/verification/signature-validator.ts +10 -3
- package/src/verification/v1.8.0.ts +24 -0
package/src/ajv.ts
CHANGED
|
@@ -1,78 +1,117 @@
|
|
|
1
|
-
import
|
|
1
|
+
import addFormats from 'ajv-formats';
|
|
2
|
+
import addKeywords from 'ajv-keywords';
|
|
2
3
|
import { parseUnits } from 'ethers';
|
|
3
4
|
import {
|
|
4
5
|
type RewardsSplitPayload,
|
|
5
6
|
type SplitRecipient,
|
|
6
7
|
type TotalSplitPayload,
|
|
7
8
|
} from './types';
|
|
9
|
+
import Ajv from 'ajv';
|
|
8
10
|
import {
|
|
9
11
|
DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
|
|
10
12
|
DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT,
|
|
11
13
|
} from './constants';
|
|
12
14
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
export const VALID_DEPOSIT_AMOUNTS = [
|
|
16
|
+
parseUnits('1', 'gwei').toString(),
|
|
17
|
+
parseUnits('32', 'gwei').toString(),
|
|
18
|
+
parseUnits('8', 'gwei').toString(),
|
|
19
|
+
parseUnits('256', 'gwei').toString(),
|
|
20
|
+
];
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
export const VALID_NON_COMPOUNDING_AMOUNTS = [
|
|
23
|
+
parseUnits('1', 'gwei').toString(),
|
|
24
|
+
parseUnits('32', 'gwei').toString(),
|
|
25
|
+
];
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
27
|
+
// They dont see defaults set in schema
|
|
28
|
+
const validateRewardsSplitRecipients = (
|
|
29
|
+
_: boolean,
|
|
30
|
+
data: RewardsSplitPayload,
|
|
31
|
+
): boolean => {
|
|
32
|
+
const obolRAFSplit =
|
|
33
|
+
data.ObolRAFSplit ?? DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT;
|
|
34
|
+
const splitPercentage = data.splitRecipients.reduce(
|
|
35
|
+
(acc: number, curr: SplitRecipient) => acc + curr.percentAllocation,
|
|
36
|
+
0,
|
|
37
|
+
);
|
|
38
|
+
return splitPercentage + obolRAFSplit === 100;
|
|
36
39
|
};
|
|
37
40
|
|
|
38
|
-
const
|
|
41
|
+
const validateTotalSplitRecipients = (
|
|
39
42
|
_: boolean,
|
|
40
|
-
data:
|
|
43
|
+
data: TotalSplitPayload,
|
|
41
44
|
): boolean => {
|
|
45
|
+
const obolRAFSplit =
|
|
46
|
+
data.ObolRAFSplit ?? DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT;
|
|
42
47
|
const splitPercentage = data.splitRecipients.reduce(
|
|
43
48
|
(acc: number, curr: SplitRecipient) => acc + curr.percentAllocation,
|
|
44
49
|
0,
|
|
45
50
|
);
|
|
46
|
-
|
|
47
|
-
? data.ObolRAFSplit
|
|
48
|
-
: 'principalRecipient' in data
|
|
49
|
-
? DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT
|
|
50
|
-
: DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT;
|
|
51
|
-
return splitPercentage + ObolRAFSplitParam === 100;
|
|
51
|
+
return splitPercentage + obolRAFSplit === 100;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
):
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
const validateUniqueAddresses = (
|
|
55
|
+
_: boolean,
|
|
56
|
+
operators: Array<{ address: string }>,
|
|
57
|
+
): boolean => {
|
|
58
|
+
if (!operators) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (operators.length < 4) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (operators.every(op => op.address === '')) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (operators.some(op => op.address.length !== 42)) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const addresses = operators.map(op => op.address);
|
|
75
|
+
const uniqueAddresses = new Set(addresses);
|
|
76
|
+
const isUnique = uniqueAddresses.size === addresses.length;
|
|
77
|
+
return isUnique;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const ajv = new Ajv({
|
|
81
|
+
allErrors: true,
|
|
82
|
+
useDefaults: true,
|
|
83
|
+
strict: false,
|
|
84
|
+
$data: true,
|
|
85
|
+
});
|
|
86
|
+
addFormats(ajv);
|
|
87
|
+
addKeywords(ajv, ['patternRequired']);
|
|
88
|
+
|
|
89
|
+
ajv.addKeyword({
|
|
90
|
+
keyword: 'validateRewardsSplitRecipients',
|
|
91
|
+
validate: validateRewardsSplitRecipients,
|
|
92
|
+
schemaType: 'boolean',
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
ajv.addKeyword({
|
|
96
|
+
keyword: 'validateTotalSplitRecipients',
|
|
97
|
+
validate: validateTotalSplitRecipients,
|
|
98
|
+
schemaType: 'boolean',
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
ajv.addKeyword({
|
|
102
|
+
keyword: 'validateUniqueAddresses',
|
|
103
|
+
validate: validateUniqueAddresses,
|
|
104
|
+
schemaType: 'boolean',
|
|
105
|
+
});
|
|
64
106
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
throw new Error(
|
|
74
|
-
`Schema compilation errors', ${validate.errors?.[0].message}`,
|
|
75
|
-
);
|
|
107
|
+
export function validatePayload<T>(data: unknown, schema: object): T {
|
|
108
|
+
const validate = ajv.compile<T>(schema);
|
|
109
|
+
const valid = validate(data);
|
|
110
|
+
if (!valid) {
|
|
111
|
+
const errors = validate.errors
|
|
112
|
+
?.map(e => `${e.instancePath} ${e.message}`)
|
|
113
|
+
.join(', ');
|
|
114
|
+
throw new Error(`Validation failed: ${errors}`);
|
|
76
115
|
}
|
|
77
|
-
return
|
|
116
|
+
return data;
|
|
78
117
|
}
|
package/src/constants.ts
CHANGED
package/src/incentiveHelpers.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type ProviderType, type SignerType, type ETH_ADDRESS } from './types';
|
|
2
2
|
import { Contract } from 'ethers';
|
|
3
3
|
import { MerkleDistributorABI } from './abi/MerkleDistributorWithDeadline';
|
|
4
|
-
import { getProvider } from './utils';
|
|
5
4
|
|
|
6
5
|
export const claimIncentivesFromMerkleDistributor = async (incentivesData: {
|
|
7
6
|
signer: SignerType;
|
|
@@ -35,18 +34,15 @@ export const claimIncentivesFromMerkleDistributor = async (incentivesData: {
|
|
|
35
34
|
};
|
|
36
35
|
|
|
37
36
|
export const isClaimedFromMerkleDistributor = async (
|
|
38
|
-
chainId: number,
|
|
39
37
|
contractAddress: ETH_ADDRESS,
|
|
40
38
|
index: number,
|
|
41
39
|
provider: ProviderType | undefined | null,
|
|
42
40
|
): Promise<boolean> => {
|
|
43
41
|
try {
|
|
44
|
-
const clientProvider = provider ?? getProvider(chainId);
|
|
45
|
-
|
|
46
42
|
const contract = new Contract(
|
|
47
43
|
contractAddress,
|
|
48
44
|
MerkleDistributorABI.abi,
|
|
49
|
-
|
|
45
|
+
provider,
|
|
50
46
|
);
|
|
51
47
|
|
|
52
48
|
const claimed = await contract.isClaimed(BigInt(index));
|
package/src/incentives.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isContractAvailable } from './utils';
|
|
2
2
|
import {
|
|
3
|
-
type
|
|
3
|
+
type ClaimableIncentives,
|
|
4
4
|
type ETH_ADDRESS,
|
|
5
5
|
FORK_NAMES,
|
|
6
6
|
type ProviderType,
|
|
@@ -126,7 +126,6 @@ export class Incentives {
|
|
|
126
126
|
index: number,
|
|
127
127
|
): Promise<boolean> {
|
|
128
128
|
return await isClaimedFromMerkleDistributor(
|
|
129
|
-
this.chainId,
|
|
130
129
|
contractAddress,
|
|
131
130
|
index,
|
|
132
131
|
this.provider,
|
|
@@ -141,9 +140,9 @@ export class Incentives {
|
|
|
141
140
|
* An example of how to use getIncentivesByAddress:
|
|
142
141
|
* [obolClient](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L250)
|
|
143
142
|
*/
|
|
144
|
-
async getIncentivesByAddress(address: string): Promise<
|
|
143
|
+
async getIncentivesByAddress(address: string): Promise<ClaimableIncentives> {
|
|
145
144
|
const network = FORK_NAMES[this.chainId];
|
|
146
|
-
const incentives:
|
|
145
|
+
const incentives: ClaimableIncentives = await this.request(
|
|
147
146
|
`/${DEFAULT_BASE_VERSION}/address/incentives/${network}/${address}`,
|
|
148
147
|
{
|
|
149
148
|
method: 'GET',
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ZeroAddress } from 'ethers';
|
|
2
1
|
import { v4 as uuidv4 } from 'uuid';
|
|
3
2
|
import { Base } from './base.js';
|
|
4
3
|
import {
|
|
@@ -16,7 +15,6 @@ import {
|
|
|
16
15
|
AVAILABLE_SPLITTER_CHAINS,
|
|
17
16
|
CHAIN_CONFIGURATION,
|
|
18
17
|
DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
|
|
19
|
-
DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT,
|
|
20
18
|
OBOL_SDK_EMAIL,
|
|
21
19
|
} from './constants.js';
|
|
22
20
|
import { ConflictError } from './errors.js';
|
|
@@ -54,6 +52,7 @@ export * from './types.js';
|
|
|
54
52
|
export * from './services.js';
|
|
55
53
|
export * from './verification/signature-validator.js';
|
|
56
54
|
export * from './verification/common.js';
|
|
55
|
+
export * from './constants.js';
|
|
57
56
|
export { Incentives } from './incentives.js';
|
|
58
57
|
|
|
59
58
|
/**
|
|
@@ -174,16 +173,16 @@ export class Client extends Base {
|
|
|
174
173
|
principalRecipient,
|
|
175
174
|
etherAmount,
|
|
176
175
|
ObolRAFSplit = DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
|
|
177
|
-
distributorFee
|
|
178
|
-
controllerAddress
|
|
179
|
-
recoveryAddress
|
|
176
|
+
distributorFee,
|
|
177
|
+
controllerAddress,
|
|
178
|
+
recoveryAddress,
|
|
180
179
|
}: RewardsSplitPayload): Promise<ClusterValidator> {
|
|
181
180
|
// This method doesnt require T&C signature
|
|
182
181
|
if (!this.signer) {
|
|
183
182
|
throw new Error('Signer is required in createObolRewardsSplit');
|
|
184
183
|
}
|
|
185
184
|
|
|
186
|
-
validatePayload(
|
|
185
|
+
const validatedPayload = validatePayload<Required<RewardsSplitPayload>>(
|
|
187
186
|
{
|
|
188
187
|
splitRecipients,
|
|
189
188
|
principalRecipient,
|
|
@@ -234,10 +233,10 @@ export class Client extends Base {
|
|
|
234
233
|
const retroActiveFundingRecipient = {
|
|
235
234
|
account:
|
|
236
235
|
CHAIN_CONFIGURATION[this.chainId].RETROACTIVE_FUNDING_ADDRESS.address,
|
|
237
|
-
percentAllocation: ObolRAFSplit,
|
|
236
|
+
percentAllocation: validatedPayload.ObolRAFSplit,
|
|
238
237
|
};
|
|
239
238
|
|
|
240
|
-
const copiedSplitRecipients = [...splitRecipients];
|
|
239
|
+
const copiedSplitRecipients = [...validatedPayload.splitRecipients];
|
|
241
240
|
copiedSplitRecipients.push(retroActiveFundingRecipient);
|
|
242
241
|
|
|
243
242
|
const { accounts, percentAllocations } = formatSplitRecipients(
|
|
@@ -249,8 +248,8 @@ export class Client extends Base {
|
|
|
249
248
|
accounts,
|
|
250
249
|
percentAllocations,
|
|
251
250
|
chainId: this.chainId,
|
|
252
|
-
distributorFee,
|
|
253
|
-
controllerAddress,
|
|
251
|
+
distributorFee: validatedPayload.distributorFee,
|
|
252
|
+
controllerAddress: validatedPayload.controllerAddress,
|
|
254
253
|
});
|
|
255
254
|
|
|
256
255
|
const isSplitterDeployed = await isContractAvailable(
|
|
@@ -265,12 +264,12 @@ export class Client extends Base {
|
|
|
265
264
|
predictedSplitterAddress,
|
|
266
265
|
accounts,
|
|
267
266
|
percentAllocations,
|
|
268
|
-
principalRecipient,
|
|
269
|
-
etherAmount,
|
|
267
|
+
principalRecipient: validatedPayload.principalRecipient,
|
|
268
|
+
etherAmount: validatedPayload.etherAmount,
|
|
270
269
|
chainId: this.chainId,
|
|
271
|
-
distributorFee,
|
|
272
|
-
controllerAddress,
|
|
273
|
-
recoveryAddress,
|
|
270
|
+
distributorFee: validatedPayload.distributorFee,
|
|
271
|
+
controllerAddress: validatedPayload.controllerAddress,
|
|
272
|
+
recoveryAddress: validatedPayload.recoveryAddress,
|
|
274
273
|
});
|
|
275
274
|
|
|
276
275
|
return { withdrawal_address, fee_recipient_address };
|
|
@@ -292,16 +291,16 @@ export class Client extends Base {
|
|
|
292
291
|
// add the example reference
|
|
293
292
|
async createObolTotalSplit({
|
|
294
293
|
splitRecipients,
|
|
295
|
-
ObolRAFSplit
|
|
296
|
-
distributorFee
|
|
297
|
-
controllerAddress
|
|
294
|
+
ObolRAFSplit,
|
|
295
|
+
distributorFee,
|
|
296
|
+
controllerAddress,
|
|
298
297
|
}: TotalSplitPayload): Promise<ClusterValidator> {
|
|
299
298
|
// This method doesnt require T&C signature
|
|
300
299
|
if (!this.signer) {
|
|
301
300
|
throw new Error('Signer is required in createObolTotalSplit');
|
|
302
301
|
}
|
|
303
302
|
|
|
304
|
-
validatePayload(
|
|
303
|
+
const validatedPayload = validatePayload<Required<TotalSplitPayload>>(
|
|
305
304
|
{
|
|
306
305
|
splitRecipients,
|
|
307
306
|
ObolRAFSplit,
|
|
@@ -333,10 +332,10 @@ export class Client extends Base {
|
|
|
333
332
|
const retroActiveFundingRecipient = {
|
|
334
333
|
account:
|
|
335
334
|
CHAIN_CONFIGURATION[this.chainId].RETROACTIVE_FUNDING_ADDRESS.address,
|
|
336
|
-
percentAllocation: ObolRAFSplit,
|
|
335
|
+
percentAllocation: validatedPayload.ObolRAFSplit,
|
|
337
336
|
};
|
|
338
337
|
|
|
339
|
-
const copiedSplitRecipients = [...splitRecipients];
|
|
338
|
+
const copiedSplitRecipients = [...validatedPayload.splitRecipients];
|
|
340
339
|
copiedSplitRecipients.push(retroActiveFundingRecipient);
|
|
341
340
|
|
|
342
341
|
const { accounts, percentAllocations } = formatSplitRecipients(
|
|
@@ -347,8 +346,8 @@ export class Client extends Base {
|
|
|
347
346
|
accounts,
|
|
348
347
|
percentAllocations,
|
|
349
348
|
chainId: this.chainId,
|
|
350
|
-
distributorFee,
|
|
351
|
-
controllerAddress,
|
|
349
|
+
distributorFee: validatedPayload.distributorFee,
|
|
350
|
+
controllerAddress: validatedPayload.controllerAddress,
|
|
352
351
|
});
|
|
353
352
|
|
|
354
353
|
const isSplitterDeployed = await isContractAvailable(
|
|
@@ -362,8 +361,8 @@ export class Client extends Base {
|
|
|
362
361
|
accounts,
|
|
363
362
|
percentAllocations,
|
|
364
363
|
chainId: this.chainId,
|
|
365
|
-
distributorFee,
|
|
366
|
-
controllerAddress,
|
|
364
|
+
distributorFee: validatedPayload.distributorFee,
|
|
365
|
+
controllerAddress: validatedPayload.controllerAddress,
|
|
367
366
|
});
|
|
368
367
|
return {
|
|
369
368
|
withdrawal_address: splitterAddress,
|
|
@@ -411,20 +410,20 @@ export class Client extends Base {
|
|
|
411
410
|
throw new Error('Signer is required in createClusterDefinition');
|
|
412
411
|
}
|
|
413
412
|
|
|
414
|
-
validatePayload(
|
|
413
|
+
const validatedCluster = validatePayload<ClusterPayload>(
|
|
414
|
+
newCluster,
|
|
415
|
+
definitionSchema,
|
|
416
|
+
);
|
|
415
417
|
|
|
416
418
|
const clusterConfig: Partial<ClusterDefinition> = {
|
|
417
|
-
...
|
|
419
|
+
...validatedCluster,
|
|
418
420
|
fork_version: this.fork_version,
|
|
419
421
|
dkg_algorithm: DKG_ALGORITHM,
|
|
420
422
|
version: CONFIG_VERSION,
|
|
421
423
|
uuid: uuidv4(),
|
|
422
424
|
timestamp: new Date().toISOString(),
|
|
423
|
-
threshold: Math.ceil((2 *
|
|
424
|
-
num_validators:
|
|
425
|
-
deposit_amounts: newCluster.deposit_amounts
|
|
426
|
-
? newCluster.deposit_amounts
|
|
427
|
-
: ['32000000000'],
|
|
425
|
+
threshold: Math.ceil((2 * validatedCluster.operators.length) / 3),
|
|
426
|
+
num_validators: validatedCluster.validators.length,
|
|
428
427
|
};
|
|
429
428
|
try {
|
|
430
429
|
const address = await this.signer.getAddress();
|
|
@@ -479,7 +478,10 @@ export class Client extends Base {
|
|
|
479
478
|
throw new Error('Signer is required in acceptClusterDefinition');
|
|
480
479
|
}
|
|
481
480
|
|
|
482
|
-
validatePayload(
|
|
481
|
+
const validatedPayload = validatePayload<OperatorPayload>(
|
|
482
|
+
operatorPayload,
|
|
483
|
+
operatorPayloadSchema,
|
|
484
|
+
);
|
|
483
485
|
|
|
484
486
|
try {
|
|
485
487
|
const address = await this.signer.getAddress();
|
|
@@ -492,11 +494,11 @@ export class Client extends Base {
|
|
|
492
494
|
const operatorENRSignature = await this.signer.signTypedData(
|
|
493
495
|
Domain(this.chainId),
|
|
494
496
|
EnrSigningTypes,
|
|
495
|
-
{ enr:
|
|
497
|
+
{ enr: validatedPayload.enr },
|
|
496
498
|
);
|
|
497
499
|
|
|
498
500
|
const operatorData: OperatorPayload = {
|
|
499
|
-
...
|
|
501
|
+
...validatedPayload,
|
|
500
502
|
address,
|
|
501
503
|
enr_signature: operatorENRSignature,
|
|
502
504
|
fork_version: this.fork_version,
|
package/src/schema.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
+
import { ZeroAddress } from 'ethers';
|
|
1
2
|
import {
|
|
2
3
|
DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
|
|
3
4
|
DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT,
|
|
4
5
|
} from './constants';
|
|
6
|
+
import { VALID_DEPOSIT_AMOUNTS, VALID_NON_COMPOUNDING_AMOUNTS } from './ajv';
|
|
5
7
|
|
|
6
8
|
export const operatorPayloadSchema = {
|
|
7
9
|
type: 'object',
|
|
8
10
|
properties: {
|
|
9
|
-
version: {
|
|
10
|
-
|
|
11
|
-
},
|
|
12
|
-
enr: {
|
|
13
|
-
type: 'string',
|
|
14
|
-
},
|
|
11
|
+
version: { type: 'string' },
|
|
12
|
+
enr: { type: 'string' },
|
|
15
13
|
},
|
|
16
14
|
required: ['version', 'enr'],
|
|
17
15
|
};
|
|
@@ -19,24 +17,20 @@ export const operatorPayloadSchema = {
|
|
|
19
17
|
export const definitionSchema = {
|
|
20
18
|
type: 'object',
|
|
21
19
|
properties: {
|
|
22
|
-
name: {
|
|
23
|
-
type: 'string',
|
|
24
|
-
},
|
|
20
|
+
name: { type: 'string' },
|
|
25
21
|
operators: {
|
|
26
22
|
type: 'array',
|
|
27
23
|
minItems: 4,
|
|
28
|
-
uniqueItems: true,
|
|
29
24
|
items: {
|
|
30
25
|
type: 'object',
|
|
31
26
|
properties: {
|
|
32
27
|
address: {
|
|
33
28
|
type: 'string',
|
|
34
|
-
minLength: 42,
|
|
35
|
-
maxLength: 42,
|
|
36
29
|
},
|
|
37
30
|
},
|
|
38
31
|
required: ['address'],
|
|
39
32
|
},
|
|
33
|
+
validateUniqueAddresses: true,
|
|
40
34
|
},
|
|
41
35
|
validators: {
|
|
42
36
|
type: 'array',
|
|
@@ -57,12 +51,39 @@ export const definitionSchema = {
|
|
|
57
51
|
},
|
|
58
52
|
},
|
|
59
53
|
deposit_amounts: {
|
|
60
|
-
type: 'array',
|
|
54
|
+
type: ['array', 'null'],
|
|
61
55
|
items: {
|
|
62
56
|
type: 'string',
|
|
63
57
|
pattern: '^[0-9]+$',
|
|
64
58
|
},
|
|
65
|
-
|
|
59
|
+
if: {
|
|
60
|
+
$data: '1/compounding',
|
|
61
|
+
},
|
|
62
|
+
then: {
|
|
63
|
+
items: {
|
|
64
|
+
enum: VALID_DEPOSIT_AMOUNTS,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
else: {
|
|
68
|
+
items: {
|
|
69
|
+
enum: VALID_NON_COMPOUNDING_AMOUNTS,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
default: null,
|
|
73
|
+
},
|
|
74
|
+
compounding: {
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
default: true,
|
|
77
|
+
},
|
|
78
|
+
target_gas_limit: {
|
|
79
|
+
type: 'number',
|
|
80
|
+
minimum: 1,
|
|
81
|
+
default: 36000000,
|
|
82
|
+
},
|
|
83
|
+
consensus_protocol: {
|
|
84
|
+
type: 'string',
|
|
85
|
+
enum: ['qbft', ''],
|
|
86
|
+
default: '',
|
|
66
87
|
},
|
|
67
88
|
},
|
|
68
89
|
required: ['name', 'operators', 'validators'],
|
|
@@ -80,9 +101,7 @@ export const totalSplitterPayloadSchema = {
|
|
|
80
101
|
type: 'string',
|
|
81
102
|
pattern: '^0x[a-fA-F0-9]{40}$',
|
|
82
103
|
},
|
|
83
|
-
percentAllocation: {
|
|
84
|
-
type: 'number',
|
|
85
|
-
},
|
|
104
|
+
percentAllocation: { type: 'number' },
|
|
86
105
|
},
|
|
87
106
|
required: ['account', 'percentAllocation'],
|
|
88
107
|
},
|
|
@@ -90,40 +109,44 @@ export const totalSplitterPayloadSchema = {
|
|
|
90
109
|
ObolRAFSplit: {
|
|
91
110
|
type: 'number',
|
|
92
111
|
minimum: DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT,
|
|
112
|
+
default: DEFAULT_RETROACTIVE_FUNDING_TOTAL_SPLIT,
|
|
93
113
|
},
|
|
94
114
|
distributorFee: {
|
|
95
115
|
type: 'number',
|
|
96
116
|
maximum: 10,
|
|
97
117
|
multipleOf: 0.01,
|
|
118
|
+
default: 0,
|
|
98
119
|
},
|
|
99
120
|
controllerAddress: {
|
|
100
121
|
type: 'string',
|
|
101
122
|
pattern: '^0x[a-fA-F0-9]{40}$',
|
|
123
|
+
default: ZeroAddress,
|
|
102
124
|
},
|
|
103
|
-
validateSplitRecipients: true,
|
|
104
125
|
},
|
|
126
|
+
validateTotalSplitRecipients: true,
|
|
105
127
|
required: ['splitRecipients'],
|
|
106
128
|
};
|
|
107
129
|
|
|
108
130
|
export const rewardsSplitterPayloadSchema = {
|
|
109
|
-
|
|
131
|
+
type: 'object',
|
|
110
132
|
properties: {
|
|
111
133
|
...totalSplitterPayloadSchema.properties,
|
|
112
134
|
ObolRAFSplit: {
|
|
113
135
|
type: 'number',
|
|
114
136
|
minimum: DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
|
|
137
|
+
default: DEFAULT_RETROACTIVE_FUNDING_REWARDS_ONLY_SPLIT,
|
|
115
138
|
},
|
|
116
139
|
recoveryAddress: {
|
|
117
140
|
type: 'string',
|
|
118
141
|
pattern: '^0x[a-fA-F0-9]{40}$',
|
|
142
|
+
default: ZeroAddress,
|
|
119
143
|
},
|
|
120
|
-
etherAmount: {
|
|
121
|
-
type: 'number',
|
|
122
|
-
},
|
|
144
|
+
etherAmount: { type: 'number' },
|
|
123
145
|
principalRecipient: {
|
|
124
146
|
type: 'string',
|
|
125
147
|
pattern: '^0x[a-fA-F0-9]{40}$',
|
|
126
148
|
},
|
|
127
149
|
},
|
|
150
|
+
validateRewardsSplitRecipients: true,
|
|
128
151
|
required: ['splitRecipients', 'principalRecipient', 'etherAmount'],
|
|
129
152
|
};
|
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;
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type Wallet,
|
|
2
3
|
type ethers,
|
|
3
4
|
type JsonRpcApiProvider,
|
|
4
5
|
type JsonRpcProvider,
|
|
@@ -104,6 +105,15 @@ export type ClusterPayload = {
|
|
|
104
105
|
|
|
105
106
|
/** The cluster partial deposits in gwei or 32000000000. */
|
|
106
107
|
deposit_amounts?: string[] | null;
|
|
108
|
+
|
|
109
|
+
/** A withdrawal mechanism with 0x02 withdrawal credentials. */
|
|
110
|
+
compounding?: boolean;
|
|
111
|
+
|
|
112
|
+
/** The target gas limit where default is 36M. */
|
|
113
|
+
target_gas_limit?: number;
|
|
114
|
+
|
|
115
|
+
/** The consensus protocol e.g qbft. */
|
|
116
|
+
consensus_protocol?: string;
|
|
107
117
|
};
|
|
108
118
|
|
|
109
119
|
/**
|
|
@@ -143,7 +153,7 @@ export interface ClusterDefinition extends ClusterPayload {
|
|
|
143
153
|
/** The consensus protocol e.g qbft. */
|
|
144
154
|
consensus_protocol?: string;
|
|
145
155
|
|
|
146
|
-
/** The target gas limit where default is
|
|
156
|
+
/** The target gas limit where default is 36M. */
|
|
147
157
|
target_gas_limit?: number;
|
|
148
158
|
|
|
149
159
|
/** A withdrawal mechanism with 0x02 withdrawal credentials. */
|
|
@@ -295,16 +305,16 @@ export type ClusterLock = {
|
|
|
295
305
|
};
|
|
296
306
|
|
|
297
307
|
/**
|
|
298
|
-
* Incentives
|
|
308
|
+
* Claimable Obol Incentives
|
|
299
309
|
*/
|
|
300
|
-
export type
|
|
310
|
+
export type ClaimableIncentives = {
|
|
301
311
|
/** Operator Address. */
|
|
302
312
|
operator_address: string;
|
|
303
313
|
|
|
304
314
|
/** The amount the recipient is entitled to. */
|
|
305
315
|
amount: string;
|
|
306
316
|
|
|
307
|
-
/** The recipient
|
|
317
|
+
/** The recipient's index in the Merkle tree. */
|
|
308
318
|
index: number;
|
|
309
319
|
|
|
310
320
|
/** The Merkle proof (an array of hashes) generated for the recipient. */
|
|
@@ -328,10 +338,15 @@ export type ProviderType =
|
|
|
328
338
|
| JsonRpcApiProvider
|
|
329
339
|
| ethers.BrowserProvider;
|
|
330
340
|
|
|
341
|
+
/**
|
|
342
|
+
* Safe Wallet Provider Types
|
|
343
|
+
*/
|
|
344
|
+
export type SafeRpcUrl = string;
|
|
345
|
+
|
|
331
346
|
/**
|
|
332
347
|
* Signer Types
|
|
333
348
|
*/
|
|
334
|
-
export type SignerType = Signer | JsonRpcSigner;
|
|
349
|
+
export type SignerType = Signer | JsonRpcSigner | Wallet;
|
|
335
350
|
|
|
336
351
|
/**
|
|
337
352
|
* claimIncentives Response
|
package/src/utils.ts
CHANGED
|
@@ -76,10 +76,13 @@ export const isContractAvailable = async (
|
|
|
76
76
|
return !!code && code !== '0x' && code !== '0x0';
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
export const getProvider = (
|
|
80
|
-
|
|
81
|
-
|
|
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(
|
|
87
|
+
return new ethers.JsonRpcProvider(resolvedRpcUrl);
|
|
85
88
|
};
|