@obolnetwork/obol-sdk 1.0.8 → 1.0.11

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 (66) hide show
  1. package/README.md +2 -1
  2. package/dist/cjs/src/ajv.js +17 -0
  3. package/dist/cjs/src/base.js +39 -0
  4. package/dist/cjs/src/constants.js +86 -0
  5. package/dist/cjs/src/errors.js +11 -0
  6. package/dist/cjs/src/hash.js +172 -0
  7. package/dist/cjs/src/index.js +158 -0
  8. package/dist/cjs/src/schema.js +72 -0
  9. package/dist/cjs/src/services.js +32 -0
  10. package/dist/cjs/src/types.js +18 -0
  11. package/dist/cjs/src/utils.js +11 -0
  12. package/dist/cjs/src/verify.js +326 -0
  13. package/dist/cjs/test/fixtures.js +101 -0
  14. package/dist/cjs/test/methods.test.js +128 -0
  15. package/dist/esm/src/ajv.js +10 -0
  16. package/dist/esm/src/base.js +35 -0
  17. package/dist/esm/src/constants.js +79 -0
  18. package/dist/esm/src/errors.js +7 -0
  19. package/dist/esm/src/hash.js +165 -0
  20. package/dist/esm/src/index.js +140 -0
  21. package/dist/esm/src/schema.js +69 -0
  22. package/dist/esm/src/services.js +28 -0
  23. package/dist/esm/src/types.js +15 -0
  24. package/dist/esm/src/utils.js +6 -0
  25. package/dist/esm/src/verify.js +295 -0
  26. package/dist/esm/test/fixtures.js +98 -0
  27. package/dist/esm/test/methods.test.js +126 -0
  28. package/dist/types/src/ajv.d.ts +2 -0
  29. package/dist/{base.d.ts → types/src/base.d.ts} +13 -14
  30. package/dist/types/src/constants.d.ts +79 -0
  31. package/dist/{errors.d.ts → types/src/errors.d.ts} +4 -5
  32. package/dist/{hash.d.ts → types/src/hash.d.ts} +56 -64
  33. package/dist/{index.d.ts → types/src/index.d.ts} +64 -49
  34. package/dist/types/src/schema.d.ts +57 -0
  35. package/dist/types/src/services.d.ts +11 -0
  36. package/dist/{types.d.ts → types/src/types.d.ts} +153 -150
  37. package/dist/types/src/utils.d.ts +2 -0
  38. package/dist/types/src/verify.d.ts +4 -0
  39. package/dist/{fixtures.d.ts → types/test/fixtures.d.ts} +61 -62
  40. package/dist/types/test/methods.test.d.ts +1 -0
  41. package/package.json +55 -21
  42. package/src/ajv.ts +11 -0
  43. package/src/base.ts +44 -0
  44. package/src/constants.ts +118 -0
  45. package/src/errors.ts +11 -0
  46. package/src/hash.ts +250 -0
  47. package/src/index.ts +160 -0
  48. package/src/schema.ts +70 -0
  49. package/src/services.ts +20 -0
  50. package/src/types.ts +211 -0
  51. package/src/utils.ts +7 -0
  52. package/src/verify.ts +479 -0
  53. package/dist/ajv.d.ts +0 -4
  54. package/dist/ajv.d.ts.map +0 -1
  55. package/dist/base.d.ts.map +0 -1
  56. package/dist/cluster.test.d.ts +0 -2
  57. package/dist/cluster.test.d.ts.map +0 -1
  58. package/dist/constants.d.ts +0 -18
  59. package/dist/constants.d.ts.map +0 -1
  60. package/dist/errors.d.ts.map +0 -1
  61. package/dist/fixtures.d.ts.map +0 -1
  62. package/dist/hash.d.ts.map +0 -1
  63. package/dist/index.d.ts.map +0 -1
  64. package/dist/index.es.js +0 -15527
  65. package/dist/index.js +0 -15531
  66. package/dist/types.d.ts.map +0 -1
package/src/index.ts ADDED
@@ -0,0 +1,160 @@
1
+ import { Signer } from "ethers";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ import { Base } from './base.js';
4
+ import { CONFLICT_ERROR_MSG, CreatorConfigHashSigningTypes, Domain, dkg_algorithm, config_version, OperatorConfigHashSigningTypes, EnrSigningTypes } from './constants.js';
5
+ import { ConflictError } from './errors.js';
6
+ import { ClusterDefintion, ClusterLock, ClusterPayload, OperatorPayload } from './types.js';
7
+ import { clusterConfigOrDefinitionHash } from './hash.js';
8
+ import { validatePayload } from './ajv.js';
9
+ import { definitionSchema, operatorPayloadSchema } from './schema.js';
10
+ export * from "./types.js";
11
+ export * from "./services.js";
12
+
13
+
14
+ /**
15
+ * Obol sdk Client can be used for creating, managing and activating distributed validators.
16
+ */
17
+ export class Client extends Base {
18
+ private signer: Signer | undefined;
19
+
20
+ /**
21
+ * @param config - Client configurations
22
+ * @param config.baseUrl - obol-api url
23
+ * @param config.chainId - Blockchain network ID
24
+ * @param signer - ethersJS Signer
25
+ * @returns Obol-SDK Client instance
26
+ *
27
+ * An example of how to instantiate obol-sdk Client:
28
+ * [obolClient](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts#L29)
29
+ */
30
+ constructor(config: { baseUrl?: string | undefined; chainId?: number | undefined }, signer?: Signer) {
31
+
32
+ super(config)
33
+ this.signer = signer
34
+ }
35
+
36
+ /**
37
+ * Creates a cluster definition which contains cluster configuration.
38
+ * @param {ClusterPayload} newCluster - The new unique cluster.
39
+ * @returns {Promise<string>} config_hash.
40
+ * @throws On duplicate entries, missing or wrong cluster keys.
41
+ *
42
+ * An example of how to use createClusterDefinition:
43
+ * [createObolCluster](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts)
44
+ */
45
+ async createClusterDefinition(newCluster: ClusterPayload): Promise<string> {
46
+ if (!this.signer) throw "Signer is required in createClusterDefinition"
47
+
48
+ validatePayload(newCluster, definitionSchema);
49
+
50
+ const clusterConfig: Partial<ClusterDefintion> = {
51
+ ...newCluster,
52
+ fork_version: this.fork_version,
53
+ dkg_algorithm: dkg_algorithm,
54
+ version: config_version,
55
+ uuid: uuidv4(),
56
+ timestamp: new Date().toISOString(),
57
+ threshold: Math.ceil((2 * newCluster.operators.length) / 3),
58
+ num_validators: newCluster.validators.length
59
+ }
60
+
61
+ try {
62
+ const address = await this.signer.getAddress();
63
+
64
+ clusterConfig.creator = { address };
65
+ clusterConfig.config_hash = clusterConfigOrDefinitionHash(clusterConfig as ClusterDefintion, true);
66
+
67
+ const creatorConfigSignature = await this.signer.signTypedData(Domain(this.chainId), CreatorConfigHashSigningTypes, { creator_config_hash: clusterConfig.config_hash });
68
+
69
+ const clusterDefinition: ClusterDefintion = await this.request(`/dv`, {
70
+ method: 'POST',
71
+ body: JSON.stringify(clusterConfig),
72
+ headers: {
73
+ Authorization: `Bearer ${creatorConfigSignature}`,
74
+ "fork-version": this.fork_version,
75
+ }
76
+
77
+ });
78
+ return clusterDefinition?.config_hash;
79
+ } catch (err: any) {
80
+ if (err?.message == CONFLICT_ERROR_MSG)
81
+ throw new ConflictError();
82
+ throw err;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Approves joining a cluster with specific configuration.
88
+ * @param {OperatorPayload} operatorPayload - The operator data including signatures.
89
+ * @param {string} configHash - The config hash of the cluster which the operator confirms joining to.
90
+ * @returns {Promise<ClusterDefintion>} The cluster definition.
91
+ * @throws On unauthorized, duplicate entries, missing keys, not found cluster or invalid data.
92
+ *
93
+ * An example of how to use acceptClusterDefinition:
94
+ * [acceptClusterDefinition](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts)
95
+ */
96
+ async acceptClusterDefinition(operatorPayload: OperatorPayload, configHash: string): Promise<ClusterDefintion> {
97
+ if (!this.signer) throw "Signer is required in acceptClusterDefinition"
98
+
99
+ validatePayload(operatorPayload, operatorPayloadSchema);
100
+
101
+ try {
102
+ const address = await this.signer.getAddress();
103
+
104
+ const operatorConfigSignature = await this.signer.signTypedData(Domain(this.chainId), OperatorConfigHashSigningTypes, { operator_config_hash: configHash });
105
+ const operatorENRSignature = await this.signer.signTypedData(Domain(this.chainId), EnrSigningTypes, { enr: operatorPayload.enr });
106
+
107
+ const operatorData: OperatorPayload = {
108
+ ...operatorPayload,
109
+ address,
110
+ enr_signature: operatorENRSignature,
111
+ fork_version: this.fork_version
112
+ }
113
+ const clusterDefinition: ClusterDefintion = await this.request(`/dv/${configHash}`, {
114
+ method: 'PUT',
115
+ body: JSON.stringify(operatorData),
116
+ headers: {
117
+ Authorization: `Bearer ${operatorConfigSignature}`,
118
+ }
119
+
120
+ });
121
+ return clusterDefinition;
122
+ } catch (err: any) {
123
+ throw err;
124
+ }
125
+ }
126
+
127
+
128
+ /**
129
+ * @param configHash - The configuration hash returned in createClusterDefinition
130
+ * @returns {Promise<ClusterDefintion>} The cluster definition for config hash
131
+ * @throws On not found config hash.
132
+ *
133
+ * An example of how to use getClusterDefinition:
134
+ * [getObolClusterDefinition](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts)
135
+ */
136
+ async getClusterDefinition(configHash: string): Promise<ClusterDefintion> {
137
+ const clusterDefinition: ClusterDefintion = await this.request(`/dv/${configHash}`, {
138
+ method: 'GET',
139
+ })
140
+
141
+ return clusterDefinition
142
+ }
143
+
144
+ /**
145
+ * @param configHash - The configuration hash in cluster-definition
146
+ * @returns {Promise<ClusterLock>} The matched cluster details (lock) from DB
147
+ * @throws On not found cluster definition or lock.
148
+ *
149
+ * An example of how to use getClusterLock:
150
+ * [getObolClusterLock](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts)
151
+ */
152
+ async getClusterLock(configHash: string): Promise<ClusterLock> {
153
+
154
+ const lock: ClusterLock = await this.request(`/lock/configHash/${configHash}`, {
155
+ method: 'GET',
156
+ })
157
+ return lock
158
+ }
159
+ }
160
+
package/src/schema.ts ADDED
@@ -0,0 +1,70 @@
1
+ export const operatorPayloadSchema = {
2
+ type: "object",
3
+ properties: {
4
+ version: {
5
+ type: "string"
6
+ },
7
+ enr: {
8
+ type: "string"
9
+ },
10
+ },
11
+ required: [
12
+ "version",
13
+ "enr",
14
+ ]
15
+ }
16
+
17
+ export const definitionSchema = {
18
+ type: "object",
19
+ properties: {
20
+ name: {
21
+ type: "string"
22
+ },
23
+ operators: {
24
+ type: "array",
25
+ minItems: 4,
26
+ uniqueItems: true,
27
+ items: {
28
+ type: "object",
29
+ properties: {
30
+ address: {
31
+ type: "string",
32
+ minLength: 42,
33
+ maxLength: 42
34
+ }
35
+ },
36
+ required: [
37
+ "address"
38
+ ]
39
+ }
40
+ },
41
+ validators: {
42
+ type: "array",
43
+ minItems: 1,
44
+ items: {
45
+ type: "object",
46
+ properties: {
47
+ fee_recipient_address: {
48
+ type: "string",
49
+ minLength: 42,
50
+ maxLength: 42
51
+ },
52
+ withdrawal_address: {
53
+ type: "string",
54
+ minLength: 42,
55
+ maxLength: 42
56
+ }
57
+ },
58
+ required: [
59
+ "fee_recipient_address",
60
+ "withdrawal_address"
61
+ ]
62
+ }
63
+ }
64
+ },
65
+ required: [
66
+ "name",
67
+ "operators",
68
+ "validators"
69
+ ]
70
+ }
@@ -0,0 +1,20 @@
1
+ import { ClusterLock } from "./types.js";
2
+ import { isValidClusterLock } from "./verify.js";
3
+
4
+ /**
5
+ * Verifies Cluster Lock's validity.
6
+ * @param lock - cluster lock
7
+ * @returns {Promise<{ result: boolean }> } boolean result to indicate if lock is valid
8
+ * @throws on missing keys or values.
9
+ *
10
+ * An example of how to use validateClusterLock:
11
+ * [validateClusterLock](https://github.com/ObolNetwork/obol-sdk-examples/blob/main/TS-Example/index.ts)
12
+ */
13
+ export const validateClusterLock = async (lock: ClusterLock): Promise<boolean> => {
14
+ try {
15
+ const isLockValid = await isValidClusterLock(lock);
16
+ return isLockValid
17
+ } catch (err: any) {
18
+ throw err;
19
+ }
20
+ }
package/src/types.ts ADDED
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Permitted ChainID's
3
+ */
4
+ export enum FORK_MAPPING {
5
+
6
+ /** Mainnet. */
7
+ "0x00000000" = 1,
8
+
9
+ /** Goerli/Prater. */
10
+ "0x00001020" = 5,
11
+
12
+ /** Gnosis Chain. */
13
+ "0x00000064" = 100,
14
+
15
+ /** Holesky. */
16
+ "0x01017000" = 17000
17
+ }
18
+
19
+ /**
20
+ * Node operator data
21
+ */
22
+ export type ClusterOperator = {
23
+
24
+ /** The operator address. */
25
+ address: string;
26
+
27
+ /** The operator ethereum node record. */
28
+ enr?: string;
29
+
30
+ /** The cluster fork_version. */
31
+ fork_version?: string;
32
+
33
+ /** The cluster version. */
34
+ version?: string;
35
+
36
+ /** The operator enr signature. */
37
+ enr_signature?: string;
38
+
39
+ /** The operator configuration signature. */
40
+ config_signature?: string;
41
+ }
42
+
43
+ /**
44
+ * A partial view of `ClusterOperator` with `enr` and `version` as required properties.
45
+ */
46
+ export type OperatorPayload = Partial<ClusterOperator> & Required<Pick<ClusterOperator, 'enr' | 'version'>>;
47
+
48
+ /**
49
+ * Cluster creator data
50
+ */
51
+ export type ClusterCreator = {
52
+
53
+ /** The creator address. */
54
+ address: string;
55
+ /** The cluster configuration signature. */
56
+ config_signature?: string;
57
+ }
58
+
59
+ /**
60
+ * Validator withdrawal configuration
61
+ */
62
+ export type ClusterValidator = {
63
+
64
+ /** The validator fee recipient address. */
65
+ fee_recipient_address: string;
66
+
67
+ /** The validator reward address. */
68
+ withdrawal_address: string;
69
+ }
70
+
71
+ /**
72
+ * Cluster configuration
73
+ */
74
+ export type ClusterPayload = {
75
+
76
+ /** The cluster name. */
77
+ name: string;
78
+
79
+ /** The cluster nodes operators addresses. */
80
+ operators: ClusterOperator[];
81
+
82
+ /** The clusters validators information. */
83
+ validators: ClusterValidator[];
84
+ };
85
+
86
+ /**
87
+ * Cluster definition data needed for dkg
88
+ */
89
+ export interface ClusterDefintion extends ClusterPayload {
90
+
91
+ /** The creator of the cluster. */
92
+ creator: ClusterCreator;
93
+
94
+ /** The cluster configuration version. */
95
+ version: string;
96
+
97
+ /** The cluster dkg algorithm. */
98
+ dkg_algorithm: string;
99
+
100
+ /** The cluster fork version. */
101
+ fork_version: string;
102
+
103
+ /** The cluster uuid. */
104
+ uuid: string;
105
+
106
+ /** The cluster creation timestamp. */
107
+ timestamp: string;
108
+
109
+ /** The cluster configuration hash. */
110
+ config_hash: string;
111
+
112
+ /** The distributed validator threshold. */
113
+ threshold: number;
114
+
115
+ /** The number of distributed validators in the cluster. */
116
+ num_validators: number;
117
+
118
+ /** The hash of the cluster definition. */
119
+ definition_hash?: string;
120
+ };
121
+
122
+ /**
123
+ * Unsigned DV Builder Registration Message
124
+ */
125
+ export type BuilderRegistrationMessage = {
126
+
127
+ /** The DV fee recipient. */
128
+ fee_recipient: string;
129
+
130
+ /** Default is 30000000. */
131
+ gas_limit: number;
132
+
133
+ /** Timestamp when generating cluster lock file. */
134
+ timestamp: number;
135
+
136
+ /** The public key of the DV. */
137
+ pubkey: string;
138
+ }
139
+
140
+ /**
141
+ * Pre-generated Signed Validator Builder Registration
142
+ */
143
+ export type BuilderRegistration = {
144
+
145
+ /** Builder registration message. */
146
+ message: BuilderRegistrationMessage;
147
+
148
+ /** BLS signature of the builder registration message. */
149
+ signature: string;
150
+ }
151
+
152
+ /**
153
+ * Required deposit data for validator activation
154
+ */
155
+ export type DepositData = {
156
+
157
+ /** The public key of the distributed validator. */
158
+ pubkey: string;
159
+
160
+ /** The 0x01 withdrawal address of the DV. */
161
+ withdrawal_credentials: string;
162
+
163
+ /** 32 ethers. */
164
+ amount: string;
165
+
166
+ /** A checksum for DepositData fields . */
167
+ deposit_data_root: string;
168
+
169
+ /** BLS signature of the deposit message. */
170
+ signature: string;
171
+ }
172
+
173
+ /**
174
+ * Required deposit data for validator activation
175
+ */
176
+ export type DistributedValidator = {
177
+
178
+ /** The public key of the distributed validator. */
179
+ distributed_public_key: string;
180
+
181
+ /** The public key of the node distributed validator share. */
182
+ public_shares: string[];
183
+
184
+ /** The required deposit data for activating the DV. */
185
+ deposit_data: Partial<DepositData>;
186
+
187
+ /** pre-generated signed validator builder registration to be sent to builder network. */
188
+ builder_registration: BuilderRegistration;
189
+ }
190
+
191
+ /**
192
+ * Cluster Details after DKG is complete
193
+ */
194
+ export type ClusterLock = {
195
+
196
+ /** The cluster definition. */
197
+ cluster_definition: ClusterDefintion;
198
+
199
+ /** The cluster distributed validators. */
200
+ distributed_validators: DistributedValidator[];
201
+
202
+ /** The cluster bls signature aggregate. */
203
+ signature_aggregate: string;
204
+
205
+ /** The hash of the cluster lock. */
206
+ lock_hash: string;
207
+
208
+ /** Node Signature for the lock hash by the node secp256k1 key. */
209
+ node_signatures: string[];
210
+ };
211
+
package/src/utils.ts ADDED
@@ -0,0 +1,7 @@
1
+ export const hexWithout0x = (hex: string): string => {
2
+ return hex.slice(2, hex.length);
3
+ };
4
+
5
+ export const strToUint8Array = (str: string): Uint8Array => {
6
+ return new TextEncoder().encode(str);
7
+ };