@obolnetwork/obol-sdk 2.2.4 → 2.4.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 +2 -2
- package/dist/cjs/src/abi/MerkleDistributorWithDeadline.js +73 -0
- package/dist/cjs/src/incentives.js +92 -0
- package/dist/cjs/src/incentivesHalpers.js +41 -0
- package/dist/cjs/src/index.js +2 -0
- package/dist/cjs/test/incentives.test.js +174 -0
- package/dist/cjs/test/methods.test.js +10 -22
- package/dist/esm/package.json +2 -2
- package/dist/esm/src/abi/MerkleDistributorWithDeadline.js +70 -0
- package/dist/esm/src/incentives.js +88 -0
- package/dist/esm/src/incentivesHalpers.js +36 -0
- package/dist/esm/src/index.js +2 -0
- package/dist/esm/test/incentives.test.js +149 -0
- package/dist/esm/test/methods.test.js +10 -22
- package/dist/types/src/abi/MerkleDistributorWithDeadline.d.ts +91 -0
- package/dist/types/src/incentives.d.ts +49 -0
- package/dist/types/src/incentivesHalpers.d.ts +13 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/types.d.ts +15 -0
- package/dist/types/test/incentives.test.d.ts +1 -0
- package/package.json +2 -2
- package/src/abi/MerkleDistributorWithDeadline.ts +70 -0
- package/src/incentives.ts +116 -0
- package/src/incentivesHalpers.ts +58 -0
- package/src/index.ts +7 -0
- package/src/types.ts +20 -0
|
@@ -0,0 +1,88 @@
|
|
|
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 { isContractAvailable } from './utils';
|
|
11
|
+
import { claimIncentivesFromMerkleDistributor, isClaimedFromMerkleDistributor, } from './incentivesHalpers';
|
|
12
|
+
import { DEFAULT_BASE_VERSION } from './constants';
|
|
13
|
+
export class Incentives {
|
|
14
|
+
constructor(signer, chainId, request) {
|
|
15
|
+
this.signer = signer;
|
|
16
|
+
this.chainId = chainId;
|
|
17
|
+
this.request = request;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Claims obol incentives from a Merkle Distributor contract.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* **⚠️ Important:** If you're storing the private key in an `.env` file, ensure it is securely managed
|
|
24
|
+
* and not pushed to version control.
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} incentivesData - The incentives data needed for claiming.
|
|
27
|
+
* @param {string} incentivesData.contractAddress - The address of the Merkle Distributor contract.
|
|
28
|
+
* @param {number} incentivesData.index - The index in the Merkle tree.
|
|
29
|
+
* @param {string} incentivesData.operatorAddress - The address of the operator.
|
|
30
|
+
* @param {number} incentivesData.amount - The amount to claim.
|
|
31
|
+
* @param {string[]} incentivesData.merkleProof - The Merkle proof.
|
|
32
|
+
* @returns {Promise<{ txHash: string }>} The transaction hash of the claim transaction.
|
|
33
|
+
* @throws Will throw an error if the contract is not available or the claim fails.
|
|
34
|
+
*
|
|
35
|
+
*/
|
|
36
|
+
claimIncentives(incentivesData) {
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
if (!this.signer) {
|
|
39
|
+
throw new Error('Signer is required in claimIncentives');
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const isContractDeployed = yield isContractAvailable(incentivesData.contractAddress, this.signer.provider);
|
|
43
|
+
if (!isContractDeployed) {
|
|
44
|
+
throw new Error(`Merkle Distributor contract is not available at address ${incentivesData.contractAddress}`);
|
|
45
|
+
}
|
|
46
|
+
const { txHash } = yield claimIncentivesFromMerkleDistributor({
|
|
47
|
+
signer: this.signer,
|
|
48
|
+
contractAddress: incentivesData.contractAddress,
|
|
49
|
+
index: incentivesData.index,
|
|
50
|
+
operatorAddress: incentivesData.operatorAddress,
|
|
51
|
+
amount: incentivesData.amount,
|
|
52
|
+
merkleProof: incentivesData.merkleProof,
|
|
53
|
+
});
|
|
54
|
+
return { txHash };
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
console.log('Error claiming incentives:', error);
|
|
58
|
+
throw new Error(`Failed to claim incentives: ${error.message}`);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Read isClaimed.
|
|
64
|
+
*
|
|
65
|
+
* @param {ETH_ADDRESS} contractAddress - Address of the Merkle Distributor Contract
|
|
66
|
+
* @param {ETH_ADDRESS} index - operator index in merkle tree
|
|
67
|
+
* @returns {Promise<boolean>} true if incentives are already claime
|
|
68
|
+
*
|
|
69
|
+
*/
|
|
70
|
+
isClaimed(contractAddress, index) {
|
|
71
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
72
|
+
return yield isClaimedFromMerkleDistributor(this.chainId, contractAddress, index);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @param address - Operator address
|
|
77
|
+
* @returns {Promise<IncentivesType>} The matched incentives from DB
|
|
78
|
+
* @throws On not found if address not found.
|
|
79
|
+
*/
|
|
80
|
+
getIncentivesByAddress(address) {
|
|
81
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
82
|
+
const incentives = yield this.request(`/${DEFAULT_BASE_VERSION}/address/incentives/${address}`, {
|
|
83
|
+
method: 'GET',
|
|
84
|
+
});
|
|
85
|
+
return incentives;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
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 { Contract } from 'ethers';
|
|
11
|
+
import { MerkleDistributorABI } from './abi/MerkleDistributorWithDeadline';
|
|
12
|
+
import { getProvider } from './utils';
|
|
13
|
+
export const claimIncentivesFromMerkleDistributor = (incentivesData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
14
|
+
try {
|
|
15
|
+
const contract = new Contract(incentivesData.contractAddress, MerkleDistributorABI.abi, incentivesData.signer);
|
|
16
|
+
const tx = yield contract.claim(BigInt(incentivesData.index), incentivesData.operatorAddress, BigInt(incentivesData.amount), incentivesData.merkleProof);
|
|
17
|
+
const receipt = yield tx.wait();
|
|
18
|
+
return { txHash: receipt.hash };
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.log('Error claiming incentives:', error);
|
|
22
|
+
throw new Error(`Failed to claim incentives: ${error.message}`);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
export const isClaimedFromMerkleDistributor = (chainId, contractAddress, index) => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
+
try {
|
|
27
|
+
const provider = getProvider(chainId);
|
|
28
|
+
const contract = new Contract(contractAddress, MerkleDistributorABI.abi, provider);
|
|
29
|
+
const claimed = yield contract.isClaimed(BigInt(index));
|
|
30
|
+
return claimed;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.log('Error checking claim status:', error);
|
|
34
|
+
throw new Error(`Failed to check claim status: ${error.message}`);
|
|
35
|
+
}
|
|
36
|
+
});
|
package/dist/esm/src/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { validatePayload } from './ajv.js';
|
|
|
17
17
|
import { definitionSchema, operatorPayloadSchema, rewardsSplitterPayloadSchema, totalSplitterPayloadSchema, } from './schema.js';
|
|
18
18
|
import { deploySplitterContract, formatSplitRecipients, handleDeployOWRAndSplitter, predictSplitterAddress, getOWRTranches, } from './splitHelpers.js';
|
|
19
19
|
import { isContractAvailable } from './utils.js';
|
|
20
|
+
import { Incentives } from './incentives.js';
|
|
20
21
|
export * from './types.js';
|
|
21
22
|
export * from './services.js';
|
|
22
23
|
export * from './verification/signature-validator.js';
|
|
@@ -38,6 +39,7 @@ export class Client extends Base {
|
|
|
38
39
|
constructor(config, signer) {
|
|
39
40
|
super(config);
|
|
40
41
|
this.signer = signer;
|
|
42
|
+
this.incentives = new Incentives(this.signer, this.chainId, this.request.bind(this));
|
|
41
43
|
}
|
|
42
44
|
/**
|
|
43
45
|
* Accepts Obol terms and conditions to be able to create or update data.
|
|
@@ -0,0 +1,149 @@
|
|
|
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
|
+
var _a, _b;
|
|
11
|
+
import { ethers, JsonRpcProvider } from 'ethers';
|
|
12
|
+
import { Client } from '../src/index';
|
|
13
|
+
import * as utils from '../src/utils';
|
|
14
|
+
import * as incentivesHelpers from '../src/incentivesHalpers';
|
|
15
|
+
import { DEFAULT_BASE_VERSION } from '../src/constants';
|
|
16
|
+
const mnemonic = (_b = (_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
|
|
17
|
+
const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
|
|
18
|
+
const provider = new JsonRpcProvider('https://ethereum-holesky.publicnode.com');
|
|
19
|
+
const wallet = new ethers.Wallet(privateKey, provider);
|
|
20
|
+
const mockSigner = wallet.connect(provider);
|
|
21
|
+
const baseUrl = 'https://obol-api-dev.gcp.obol.tech';
|
|
22
|
+
global.fetch = jest.fn();
|
|
23
|
+
describe('Client.incentives', () => {
|
|
24
|
+
let clientInstance;
|
|
25
|
+
const mockIncentivesData = {
|
|
26
|
+
contractAddress: '0x1234567890abcdef1234567890abcdef12345678',
|
|
27
|
+
index: 5,
|
|
28
|
+
operatorAddress: '0xabcdef1234567890abcdef1234567890abcdef12',
|
|
29
|
+
amount: 1000000000000000000,
|
|
30
|
+
merkleProof: [
|
|
31
|
+
'0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
|
|
32
|
+
'0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890',
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
jest.clearAllMocks();
|
|
37
|
+
clientInstance = new Client({ baseUrl, chainId: 17000 }, mockSigner);
|
|
38
|
+
global.fetch.mockReset();
|
|
39
|
+
});
|
|
40
|
+
test('claimIncentives should throw an error without signer', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
41
|
+
const clientWithoutSigner = new Client({
|
|
42
|
+
baseUrl,
|
|
43
|
+
chainId: 17000,
|
|
44
|
+
});
|
|
45
|
+
yield expect(clientWithoutSigner.incentives.claimIncentives(mockIncentivesData)).rejects.toThrow('Signer is required in claimIncentives');
|
|
46
|
+
}));
|
|
47
|
+
test('claimIncentives should throw an error if contract is not available', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
48
|
+
jest
|
|
49
|
+
.spyOn(utils, 'isContractAvailable')
|
|
50
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(false); }));
|
|
51
|
+
yield expect(clientInstance.incentives.claimIncentives(mockIncentivesData)).rejects.toThrow(`Merkle Distributor contract is not available at address ${mockIncentivesData.contractAddress}`);
|
|
52
|
+
}));
|
|
53
|
+
test('claimIncentives should return txHash on successful claim', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
+
const mockTxHash = '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
|
|
55
|
+
jest
|
|
56
|
+
.spyOn(utils, 'isContractAvailable')
|
|
57
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
|
|
58
|
+
jest
|
|
59
|
+
.spyOn(incentivesHelpers, 'claimIncentivesFromMerkleDistributor')
|
|
60
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve({ txHash: mockTxHash }); }));
|
|
61
|
+
const result = yield clientInstance.incentives.claimIncentives(mockIncentivesData);
|
|
62
|
+
expect(result).toEqual({ txHash: mockTxHash });
|
|
63
|
+
expect(incentivesHelpers.claimIncentivesFromMerkleDistributor).toHaveBeenCalledWith({
|
|
64
|
+
signer: mockSigner,
|
|
65
|
+
contractAddress: mockIncentivesData.contractAddress,
|
|
66
|
+
index: mockIncentivesData.index,
|
|
67
|
+
operatorAddress: mockIncentivesData.operatorAddress,
|
|
68
|
+
amount: mockIncentivesData.amount,
|
|
69
|
+
merkleProof: mockIncentivesData.merkleProof,
|
|
70
|
+
});
|
|
71
|
+
}));
|
|
72
|
+
test('claimIncentives should throw an error if helper function fails', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
73
|
+
jest
|
|
74
|
+
.spyOn(utils, 'isContractAvailable')
|
|
75
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
|
|
76
|
+
jest
|
|
77
|
+
.spyOn(incentivesHelpers, 'claimIncentivesFromMerkleDistributor')
|
|
78
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
79
|
+
throw new Error('Helper function error');
|
|
80
|
+
}));
|
|
81
|
+
yield expect(clientInstance.incentives.claimIncentives(mockIncentivesData)).rejects.toThrow('Failed to claim incentives: Helper function error');
|
|
82
|
+
}));
|
|
83
|
+
test('incentives should be initialized with the same chainId as client', () => {
|
|
84
|
+
const customChainId = 5;
|
|
85
|
+
const clientWithCustomChain = new Client({ baseUrl, chainId: customChainId }, mockSigner);
|
|
86
|
+
expect(clientWithCustomChain.incentives.chainId).toBe(customChainId);
|
|
87
|
+
});
|
|
88
|
+
test('isClaimed should return true when incentive is claimed', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
89
|
+
jest
|
|
90
|
+
.spyOn(incentivesHelpers, 'isClaimedFromMerkleDistributor')
|
|
91
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
|
|
92
|
+
const result = yield clientInstance.incentives.isClaimed(mockIncentivesData.contractAddress, mockIncentivesData.index);
|
|
93
|
+
expect(result).toBe(true);
|
|
94
|
+
expect(incentivesHelpers.isClaimedFromMerkleDistributor).toHaveBeenCalledWith(clientInstance.incentives.chainId, mockIncentivesData.contractAddress, mockIncentivesData.index);
|
|
95
|
+
}));
|
|
96
|
+
test('isClaimed should return false when incentive is not claimed', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
97
|
+
jest
|
|
98
|
+
.spyOn(incentivesHelpers, 'isClaimedFromMerkleDistributor')
|
|
99
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(false); }));
|
|
100
|
+
const result = yield clientInstance.incentives.isClaimed(mockIncentivesData.contractAddress, mockIncentivesData.index);
|
|
101
|
+
expect(result).toBe(false);
|
|
102
|
+
}));
|
|
103
|
+
test('isClaimed should throw an error if helper function fails', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
104
|
+
jest
|
|
105
|
+
.spyOn(incentivesHelpers, 'isClaimedFromMerkleDistributor')
|
|
106
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
107
|
+
throw new Error('Helper function error');
|
|
108
|
+
}));
|
|
109
|
+
yield expect(clientInstance.incentives.isClaimed(mockIncentivesData.contractAddress, mockIncentivesData.index)).rejects.toThrow('Helper function error');
|
|
110
|
+
}));
|
|
111
|
+
test('isClaimed should work with a client without signer', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
112
|
+
// Create a client without a signer
|
|
113
|
+
const clientWithoutSigner = new Client({
|
|
114
|
+
baseUrl,
|
|
115
|
+
chainId: 17000,
|
|
116
|
+
});
|
|
117
|
+
jest
|
|
118
|
+
.spyOn(incentivesHelpers, 'isClaimedFromMerkleDistributor')
|
|
119
|
+
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
|
|
120
|
+
const result = yield clientWithoutSigner.incentives.isClaimed(mockIncentivesData.contractAddress, mockIncentivesData.index);
|
|
121
|
+
expect(result).toBe(true);
|
|
122
|
+
expect(incentivesHelpers.isClaimedFromMerkleDistributor).toHaveBeenCalledWith(clientWithoutSigner.incentives.chainId, mockIncentivesData.contractAddress, mockIncentivesData.index);
|
|
123
|
+
}));
|
|
124
|
+
test('getIncentivesByAddress should make the correct API request', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
125
|
+
const mockAddress = '0x1234567890abcdef1234567890abcdef12345678';
|
|
126
|
+
const mockIncentives = {
|
|
127
|
+
operator_address: '0x8c00157cae72c4ed6a1f8bfb60205601f0252e26',
|
|
128
|
+
amount: '100',
|
|
129
|
+
index: 1,
|
|
130
|
+
merkle_proof: ['hash1', 'hash2'],
|
|
131
|
+
contract_address: '0xContract',
|
|
132
|
+
};
|
|
133
|
+
global.fetch.mockResolvedValueOnce({
|
|
134
|
+
ok: true,
|
|
135
|
+
status: 200,
|
|
136
|
+
json: () => __awaiter(void 0, void 0, void 0, function* () { return mockIncentives; }),
|
|
137
|
+
headers: new Headers(),
|
|
138
|
+
});
|
|
139
|
+
const result = yield clientInstance.incentives.getIncentivesByAddress(mockAddress);
|
|
140
|
+
expect(result).toEqual(mockIncentives);
|
|
141
|
+
expect(global.fetch).toHaveBeenCalledWith(`${baseUrl}/${DEFAULT_BASE_VERSION}/address/incentives/${mockAddress}`, expect.objectContaining({ method: 'GET' }));
|
|
142
|
+
}));
|
|
143
|
+
test('getIncentivesByAddress should handle API errors', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
144
|
+
const mockAddress = '0x1234567890abcdef1234567890abcdef12345678';
|
|
145
|
+
global.fetch.mockRejectedValue(new Error('Network error'));
|
|
146
|
+
yield expect(clientInstance.incentives.getIncentivesByAddress(mockAddress)).rejects.toThrow();
|
|
147
|
+
expect(global.fetch).toHaveBeenCalled();
|
|
148
|
+
}));
|
|
149
|
+
});
|
|
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
var _a, _b;
|
|
10
11
|
import { ethers, JsonRpcProvider } from 'ethers';
|
|
11
12
|
import { Client, validateClusterLock } from '../src/index';
|
|
12
13
|
import { clusterConfigV1X7, clusterConfigV1X8, clusterLockV1X10, clusterLockV1X6, clusterLockV1X7, clusterLockV1X8, clusterLockWithCompoundingWithdrawals, clusterLockWithSafe, nullDepositAmountsClusterLockV1X8, } from './fixtures.js';
|
|
@@ -19,15 +20,14 @@ import { hashTermsAndConditions } from '../src/verification/termsAndConditions';
|
|
|
19
20
|
import * as utils from '../src/utils';
|
|
20
21
|
import * as splitsHelpers from '../src/splitHelpers';
|
|
21
22
|
jest.setTimeout(20000);
|
|
23
|
+
const mnemonic = (_b = (_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
|
|
24
|
+
const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
|
|
25
|
+
const provider = new JsonRpcProvider('https://ethereum-holesky.publicnode.com');
|
|
26
|
+
const wallet = new ethers.Wallet(privateKey, provider);
|
|
27
|
+
const mockSigner = wallet.connect(provider);
|
|
22
28
|
/* eslint no-new: 0 */
|
|
23
29
|
describe('Cluster Client', () => {
|
|
24
|
-
var _a, _b;
|
|
25
30
|
const mockConfigHash = '0x1f6c94e6c070393a68c1aa6073a21cb1fd57f0e14d2a475a2958990ab728c2fd';
|
|
26
|
-
const mnemonic = (_b = (_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
|
|
27
|
-
const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
|
|
28
|
-
const provider = new JsonRpcProvider('https://ethereum-holesky.publicnode.com');
|
|
29
|
-
const wallet = new ethers.Wallet(privateKey, provider);
|
|
30
|
-
const mockSigner = wallet.connect(provider);
|
|
31
31
|
const clientInstance = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
|
|
32
32
|
test('createTermsAndConditions should return "successful authorization"', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
33
|
clientInstance['request'] = jest
|
|
@@ -92,12 +92,12 @@ describe('Cluster Client', () => {
|
|
|
92
92
|
}
|
|
93
93
|
}));
|
|
94
94
|
test('getClusterdefinition should return cluster definition if config hash exist', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
95
|
-
var
|
|
95
|
+
var _a;
|
|
96
96
|
clientInstance['request'] = jest
|
|
97
97
|
.fn()
|
|
98
98
|
.mockReturnValue(Promise.resolve(clusterLockV1X8.cluster_definition));
|
|
99
99
|
const clusterDefinition = yield clientInstance.getClusterDefinition(clusterLockV1X8.cluster_definition.config_hash);
|
|
100
|
-
expect((
|
|
100
|
+
expect((_a = clusterDefinition.deposit_amounts) === null || _a === void 0 ? void 0 : _a.length).toEqual(clusterLockV1X8.cluster_definition.deposit_amounts.length);
|
|
101
101
|
expect(clusterDefinition.config_hash).toEqual(clusterLockV1X8.cluster_definition.config_hash);
|
|
102
102
|
}));
|
|
103
103
|
test('getClusterLock should return lockFile if exist', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -208,9 +208,8 @@ describe('Cluster Client without a signer', () => {
|
|
|
208
208
|
}));
|
|
209
209
|
});
|
|
210
210
|
describe('createObolRewardsSplit', () => {
|
|
211
|
-
let clientInstance, clientInstanceWithourSigner, mockSplitRecipients, mockPrincipalRecipient, mockEtherAmount
|
|
211
|
+
let clientInstance, clientInstanceWithourSigner, mockSplitRecipients, mockPrincipalRecipient, mockEtherAmount;
|
|
212
212
|
beforeAll(() => {
|
|
213
|
-
var _a, _b;
|
|
214
213
|
jest
|
|
215
214
|
.spyOn(utils, 'isContractAvailable')
|
|
216
215
|
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
|
|
@@ -223,11 +222,6 @@ describe('createObolRewardsSplit', () => {
|
|
|
223
222
|
fee_recipient_address: '0xFeeRecipientAddress',
|
|
224
223
|
});
|
|
225
224
|
}));
|
|
226
|
-
const mnemonic = (_b = (_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
|
|
227
|
-
const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
|
|
228
|
-
const provider = new JsonRpcProvider('https://ethereum-holesky.publicnode.com');
|
|
229
|
-
const wallet = new ethers.Wallet(privateKey, provider);
|
|
230
|
-
mockSigner = wallet.connect(provider);
|
|
231
225
|
clientInstance = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
|
|
232
226
|
clientInstanceWithourSigner = new Client({
|
|
233
227
|
baseUrl: 'https://obol-api-dev.gcp.obol.tech',
|
|
@@ -305,9 +299,8 @@ describe('createObolRewardsSplit', () => {
|
|
|
305
299
|
}));
|
|
306
300
|
});
|
|
307
301
|
describe('createObolTotalSplit', () => {
|
|
308
|
-
let clientInstanceWithourSigner, mockSplitRecipients,
|
|
302
|
+
let clientInstanceWithourSigner, mockSplitRecipients, clientInstance;
|
|
309
303
|
beforeAll(() => {
|
|
310
|
-
var _a, _b;
|
|
311
304
|
jest
|
|
312
305
|
.spyOn(utils, 'isContractAvailable')
|
|
313
306
|
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve(true); }));
|
|
@@ -317,11 +310,6 @@ describe('createObolTotalSplit', () => {
|
|
|
317
310
|
jest
|
|
318
311
|
.spyOn(splitsHelpers, 'deploySplitterContract')
|
|
319
312
|
.mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () { return yield Promise.resolve('0xSplitterAddress'); }));
|
|
320
|
-
const mnemonic = (_b = (_a = ethers.Wallet.createRandom().mnemonic) === null || _a === void 0 ? void 0 : _a.phrase) !== null && _b !== void 0 ? _b : '';
|
|
321
|
-
const privateKey = ethers.Wallet.fromPhrase(mnemonic).privateKey;
|
|
322
|
-
const provider = new JsonRpcProvider('https://ethereum-holesky.publicnode.com');
|
|
323
|
-
const wallet = new ethers.Wallet(privateKey, provider);
|
|
324
|
-
mockSigner = wallet.connect(provider);
|
|
325
313
|
clientInstance = new Client({ baseUrl: 'https://obol-api-dev.gcp.obol.tech', chainId: 17000 }, mockSigner);
|
|
326
314
|
clientInstanceWithourSigner = new Client({
|
|
327
315
|
baseUrl: 'https://obol-api-dev.gcp.obol.tech',
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export declare const MerkleDistributorABI: {
|
|
2
|
+
abi: readonly [{
|
|
3
|
+
readonly inputs: readonly [{
|
|
4
|
+
readonly internalType: "address";
|
|
5
|
+
readonly name: "token_";
|
|
6
|
+
readonly type: "address";
|
|
7
|
+
}, {
|
|
8
|
+
readonly internalType: "bytes32";
|
|
9
|
+
readonly name: "merkleRoot_";
|
|
10
|
+
readonly type: "bytes32";
|
|
11
|
+
}];
|
|
12
|
+
readonly stateMutability: "nonpayable";
|
|
13
|
+
readonly type: "constructor";
|
|
14
|
+
}, {
|
|
15
|
+
readonly anonymous: false;
|
|
16
|
+
readonly inputs: readonly [{
|
|
17
|
+
readonly indexed: false;
|
|
18
|
+
readonly internalType: "uint256";
|
|
19
|
+
readonly name: "index";
|
|
20
|
+
readonly type: "uint256";
|
|
21
|
+
}, {
|
|
22
|
+
readonly indexed: false;
|
|
23
|
+
readonly internalType: "address";
|
|
24
|
+
readonly name: "account";
|
|
25
|
+
readonly type: "address";
|
|
26
|
+
}, {
|
|
27
|
+
readonly indexed: false;
|
|
28
|
+
readonly internalType: "uint256";
|
|
29
|
+
readonly name: "amount";
|
|
30
|
+
readonly type: "uint256";
|
|
31
|
+
}];
|
|
32
|
+
readonly name: "Claimed";
|
|
33
|
+
readonly type: "event";
|
|
34
|
+
}, {
|
|
35
|
+
readonly inputs: readonly [{
|
|
36
|
+
readonly internalType: "uint256";
|
|
37
|
+
readonly name: "index";
|
|
38
|
+
readonly type: "uint256";
|
|
39
|
+
}, {
|
|
40
|
+
readonly internalType: "address";
|
|
41
|
+
readonly name: "account";
|
|
42
|
+
readonly type: "address";
|
|
43
|
+
}, {
|
|
44
|
+
readonly internalType: "uint256";
|
|
45
|
+
readonly name: "amount";
|
|
46
|
+
readonly type: "uint256";
|
|
47
|
+
}, {
|
|
48
|
+
readonly internalType: "bytes32[]";
|
|
49
|
+
readonly name: "merkleProof";
|
|
50
|
+
readonly type: "bytes32[]";
|
|
51
|
+
}];
|
|
52
|
+
readonly name: "claim";
|
|
53
|
+
readonly outputs: readonly [];
|
|
54
|
+
readonly stateMutability: "nonpayable";
|
|
55
|
+
readonly type: "function";
|
|
56
|
+
}, {
|
|
57
|
+
readonly inputs: readonly [{
|
|
58
|
+
readonly internalType: "uint256";
|
|
59
|
+
readonly name: "index";
|
|
60
|
+
readonly type: "uint256";
|
|
61
|
+
}];
|
|
62
|
+
readonly name: "isClaimed";
|
|
63
|
+
readonly outputs: readonly [{
|
|
64
|
+
readonly internalType: "bool";
|
|
65
|
+
readonly name: "";
|
|
66
|
+
readonly type: "bool";
|
|
67
|
+
}];
|
|
68
|
+
readonly stateMutability: "view";
|
|
69
|
+
readonly type: "function";
|
|
70
|
+
}, {
|
|
71
|
+
readonly inputs: readonly [];
|
|
72
|
+
readonly name: "merkleRoot";
|
|
73
|
+
readonly outputs: readonly [{
|
|
74
|
+
readonly internalType: "bytes32";
|
|
75
|
+
readonly name: "";
|
|
76
|
+
readonly type: "bytes32";
|
|
77
|
+
}];
|
|
78
|
+
readonly stateMutability: "view";
|
|
79
|
+
readonly type: "function";
|
|
80
|
+
}, {
|
|
81
|
+
readonly inputs: readonly [];
|
|
82
|
+
readonly name: "token";
|
|
83
|
+
readonly outputs: readonly [{
|
|
84
|
+
readonly internalType: "address";
|
|
85
|
+
readonly name: "";
|
|
86
|
+
readonly type: "address";
|
|
87
|
+
}];
|
|
88
|
+
readonly stateMutability: "view";
|
|
89
|
+
readonly type: "function";
|
|
90
|
+
}];
|
|
91
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type Signer } from 'ethers';
|
|
2
|
+
import { type Incentives as IncentivesType, type ETH_ADDRESS } from './types';
|
|
3
|
+
export declare class Incentives {
|
|
4
|
+
private readonly signer;
|
|
5
|
+
chainId: number;
|
|
6
|
+
private readonly request;
|
|
7
|
+
constructor(signer: Signer | undefined, chainId: number, request: (endpoint: string, options?: RequestInit) => Promise<any>);
|
|
8
|
+
/**
|
|
9
|
+
* Claims obol incentives from a Merkle Distributor contract.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* **⚠️ Important:** If you're storing the private key in an `.env` file, ensure it is securely managed
|
|
13
|
+
* and not pushed to version control.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} incentivesData - The incentives data needed for claiming.
|
|
16
|
+
* @param {string} incentivesData.contractAddress - The address of the Merkle Distributor contract.
|
|
17
|
+
* @param {number} incentivesData.index - The index in the Merkle tree.
|
|
18
|
+
* @param {string} incentivesData.operatorAddress - The address of the operator.
|
|
19
|
+
* @param {number} incentivesData.amount - The amount to claim.
|
|
20
|
+
* @param {string[]} incentivesData.merkleProof - The Merkle proof.
|
|
21
|
+
* @returns {Promise<{ txHash: string }>} The transaction hash of the claim transaction.
|
|
22
|
+
* @throws Will throw an error if the contract is not available or the claim fails.
|
|
23
|
+
*
|
|
24
|
+
*/
|
|
25
|
+
claimIncentives(incentivesData: {
|
|
26
|
+
contractAddress: ETH_ADDRESS;
|
|
27
|
+
index: number;
|
|
28
|
+
operatorAddress: ETH_ADDRESS;
|
|
29
|
+
amount: number;
|
|
30
|
+
merkleProof: string[];
|
|
31
|
+
}): Promise<{
|
|
32
|
+
txHash: string;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* Read isClaimed.
|
|
36
|
+
*
|
|
37
|
+
* @param {ETH_ADDRESS} contractAddress - Address of the Merkle Distributor Contract
|
|
38
|
+
* @param {ETH_ADDRESS} index - operator index in merkle tree
|
|
39
|
+
* @returns {Promise<boolean>} true if incentives are already claime
|
|
40
|
+
*
|
|
41
|
+
*/
|
|
42
|
+
isClaimed(contractAddress: ETH_ADDRESS, index: number): Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* @param address - Operator address
|
|
45
|
+
* @returns {Promise<IncentivesType>} The matched incentives from DB
|
|
46
|
+
* @throws On not found if address not found.
|
|
47
|
+
*/
|
|
48
|
+
getIncentivesByAddress(address: string): Promise<IncentivesType>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ETH_ADDRESS } from './types';
|
|
2
|
+
import { type Signer } from 'ethers';
|
|
3
|
+
export declare const claimIncentivesFromMerkleDistributor: (incentivesData: {
|
|
4
|
+
signer: Signer;
|
|
5
|
+
contractAddress: ETH_ADDRESS;
|
|
6
|
+
index: number;
|
|
7
|
+
operatorAddress: ETH_ADDRESS;
|
|
8
|
+
amount: number;
|
|
9
|
+
merkleProof: string[];
|
|
10
|
+
}) => Promise<{
|
|
11
|
+
txHash: string;
|
|
12
|
+
}>;
|
|
13
|
+
export declare const isClaimedFromMerkleDistributor: (chainId: number, contractAddress: ETH_ADDRESS, index: number) => Promise<boolean>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Signer } from 'ethers';
|
|
2
2
|
import { Base } from './base.js';
|
|
3
3
|
import { type RewardsSplitPayload, type ClusterDefinition, type ClusterLock, type ClusterPayload, type OperatorPayload, type TotalSplitPayload, type ClusterValidator, type ETH_ADDRESS, type OWRTranches } from './types.js';
|
|
4
|
+
import { Incentives } from './incentives.js';
|
|
4
5
|
export * from './types.js';
|
|
5
6
|
export * from './services.js';
|
|
6
7
|
export * from './verification/signature-validator.js';
|
|
@@ -10,6 +11,7 @@ export * from './verification/common.js';
|
|
|
10
11
|
*/
|
|
11
12
|
export declare class Client extends Base {
|
|
12
13
|
private readonly signer;
|
|
14
|
+
incentives: Incentives;
|
|
13
15
|
/**
|
|
14
16
|
* @param config - Client configurations
|
|
15
17
|
* @param config.baseUrl - obol-api url
|
|
@@ -211,6 +211,21 @@ export type ClusterLock = {
|
|
|
211
211
|
/** Node Signature for the lock hash by the node secp256k1 key. */
|
|
212
212
|
node_signatures?: string[];
|
|
213
213
|
};
|
|
214
|
+
/**
|
|
215
|
+
* Incentives
|
|
216
|
+
*/
|
|
217
|
+
export type Incentives = {
|
|
218
|
+
/** Operator Address. */
|
|
219
|
+
operator_address: string;
|
|
220
|
+
/** The amount the recipient is entitled to. */
|
|
221
|
+
amount: string;
|
|
222
|
+
/** The recipient’s index in the Merkle tree. */
|
|
223
|
+
index: number;
|
|
224
|
+
/** The Merkle proof (an array of hashes) generated for the recipient. */
|
|
225
|
+
merkle_proof: string[];
|
|
226
|
+
/** The MerkleDistributor contract address. */
|
|
227
|
+
contract_address: string;
|
|
228
|
+
};
|
|
214
229
|
/**
|
|
215
230
|
* String expected to be Ethereum Address
|
|
216
231
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obolnetwork/obol-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "A package for creating Distributed Validators using the Obol API.",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/obolnetwork/obol-sdk/issues"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json",
|
|
16
16
|
"build:clean": "rm -rf ./dist",
|
|
17
17
|
"build": "npm-run-all build:clean compile",
|
|
18
|
-
"test": "jest ./test
|
|
18
|
+
"test": "jest ./test/*.test.ts",
|
|
19
19
|
"generate-typedoc": "typedoc",
|
|
20
20
|
"npm:publish": "npm publish --tag latest",
|
|
21
21
|
"release": "release-it",
|