@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,70 @@
|
|
|
1
|
+
export const MerkleDistributorABI = {
|
|
2
|
+
abi: [
|
|
3
|
+
{
|
|
4
|
+
inputs: [
|
|
5
|
+
{ internalType: 'address', name: 'token_', type: 'address' },
|
|
6
|
+
{ internalType: 'bytes32', name: 'merkleRoot_', type: 'bytes32' },
|
|
7
|
+
],
|
|
8
|
+
stateMutability: 'nonpayable',
|
|
9
|
+
type: 'constructor',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
anonymous: false,
|
|
13
|
+
inputs: [
|
|
14
|
+
{
|
|
15
|
+
indexed: false,
|
|
16
|
+
internalType: 'uint256',
|
|
17
|
+
name: 'index',
|
|
18
|
+
type: 'uint256',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
indexed: false,
|
|
22
|
+
internalType: 'address',
|
|
23
|
+
name: 'account',
|
|
24
|
+
type: 'address',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
indexed: false,
|
|
28
|
+
internalType: 'uint256',
|
|
29
|
+
name: 'amount',
|
|
30
|
+
type: 'uint256',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
name: 'Claimed',
|
|
34
|
+
type: 'event',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
inputs: [
|
|
38
|
+
{ internalType: 'uint256', name: 'index', type: 'uint256' },
|
|
39
|
+
{ internalType: 'address', name: 'account', type: 'address' },
|
|
40
|
+
{ internalType: 'uint256', name: 'amount', type: 'uint256' },
|
|
41
|
+
{ internalType: 'bytes32[]', name: 'merkleProof', type: 'bytes32[]' },
|
|
42
|
+
],
|
|
43
|
+
name: 'claim',
|
|
44
|
+
outputs: [],
|
|
45
|
+
stateMutability: 'nonpayable',
|
|
46
|
+
type: 'function',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
inputs: [{ internalType: 'uint256', name: 'index', type: 'uint256' }],
|
|
50
|
+
name: 'isClaimed',
|
|
51
|
+
outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
|
|
52
|
+
stateMutability: 'view',
|
|
53
|
+
type: 'function',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
inputs: [],
|
|
57
|
+
name: 'merkleRoot',
|
|
58
|
+
outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }],
|
|
59
|
+
stateMutability: 'view',
|
|
60
|
+
type: 'function',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
inputs: [],
|
|
64
|
+
name: 'token',
|
|
65
|
+
outputs: [{ internalType: 'address', name: '', type: 'address' }],
|
|
66
|
+
stateMutability: 'view',
|
|
67
|
+
type: 'function',
|
|
68
|
+
},
|
|
69
|
+
] as const,
|
|
70
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type Provider, type Signer } from 'ethers';
|
|
2
|
+
import { isContractAvailable } from './utils';
|
|
3
|
+
import { type Incentives as IncentivesType, type ETH_ADDRESS } from './types';
|
|
4
|
+
import {
|
|
5
|
+
claimIncentivesFromMerkleDistributor,
|
|
6
|
+
isClaimedFromMerkleDistributor,
|
|
7
|
+
} from './incentivesHalpers';
|
|
8
|
+
import { DEFAULT_BASE_VERSION } from './constants';
|
|
9
|
+
|
|
10
|
+
export class Incentives {
|
|
11
|
+
private readonly signer: Signer | undefined;
|
|
12
|
+
public chainId: number;
|
|
13
|
+
private readonly request: (
|
|
14
|
+
endpoint: string,
|
|
15
|
+
options?: RequestInit,
|
|
16
|
+
) => Promise<any>;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
signer: Signer | undefined,
|
|
20
|
+
chainId: number,
|
|
21
|
+
request: (endpoint: string, options?: RequestInit) => Promise<any>,
|
|
22
|
+
) {
|
|
23
|
+
this.signer = signer;
|
|
24
|
+
this.chainId = chainId;
|
|
25
|
+
this.request = request;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Claims obol incentives from a Merkle Distributor contract.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* **⚠️ Important:** If you're storing the private key in an `.env` file, ensure it is securely managed
|
|
33
|
+
* and not pushed to version control.
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} incentivesData - The incentives data needed for claiming.
|
|
36
|
+
* @param {string} incentivesData.contractAddress - The address of the Merkle Distributor contract.
|
|
37
|
+
* @param {number} incentivesData.index - The index in the Merkle tree.
|
|
38
|
+
* @param {string} incentivesData.operatorAddress - The address of the operator.
|
|
39
|
+
* @param {number} incentivesData.amount - The amount to claim.
|
|
40
|
+
* @param {string[]} incentivesData.merkleProof - The Merkle proof.
|
|
41
|
+
* @returns {Promise<{ txHash: string }>} The transaction hash of the claim transaction.
|
|
42
|
+
* @throws Will throw an error if the contract is not available or the claim fails.
|
|
43
|
+
*
|
|
44
|
+
*/
|
|
45
|
+
async claimIncentives(incentivesData: {
|
|
46
|
+
contractAddress: ETH_ADDRESS;
|
|
47
|
+
index: number;
|
|
48
|
+
operatorAddress: ETH_ADDRESS;
|
|
49
|
+
amount: number;
|
|
50
|
+
merkleProof: string[];
|
|
51
|
+
}): Promise<{ txHash: string }> {
|
|
52
|
+
if (!this.signer) {
|
|
53
|
+
throw new Error('Signer is required in claimIncentives');
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const isContractDeployed = await isContractAvailable(
|
|
57
|
+
incentivesData.contractAddress,
|
|
58
|
+
this.signer.provider as Provider,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (!isContractDeployed) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Merkle Distributor contract is not available at address ${incentivesData.contractAddress}`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const { txHash } = await claimIncentivesFromMerkleDistributor({
|
|
68
|
+
signer: this.signer,
|
|
69
|
+
contractAddress: incentivesData.contractAddress,
|
|
70
|
+
index: incentivesData.index,
|
|
71
|
+
operatorAddress: incentivesData.operatorAddress,
|
|
72
|
+
amount: incentivesData.amount,
|
|
73
|
+
merkleProof: incentivesData.merkleProof,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return { txHash };
|
|
77
|
+
} catch (error: any) {
|
|
78
|
+
console.log('Error claiming incentives:', error);
|
|
79
|
+
throw new Error(`Failed to claim incentives: ${error.message}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Read isClaimed.
|
|
85
|
+
*
|
|
86
|
+
* @param {ETH_ADDRESS} contractAddress - Address of the Merkle Distributor Contract
|
|
87
|
+
* @param {ETH_ADDRESS} index - operator index in merkle tree
|
|
88
|
+
* @returns {Promise<boolean>} true if incentives are already claime
|
|
89
|
+
*
|
|
90
|
+
*/
|
|
91
|
+
async isClaimed(
|
|
92
|
+
contractAddress: ETH_ADDRESS,
|
|
93
|
+
index: number,
|
|
94
|
+
): Promise<boolean> {
|
|
95
|
+
return await isClaimedFromMerkleDistributor(
|
|
96
|
+
this.chainId,
|
|
97
|
+
contractAddress,
|
|
98
|
+
index,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param address - Operator address
|
|
104
|
+
* @returns {Promise<IncentivesType>} The matched incentives from DB
|
|
105
|
+
* @throws On not found if address not found.
|
|
106
|
+
*/
|
|
107
|
+
async getIncentivesByAddress(address: string): Promise<IncentivesType> {
|
|
108
|
+
const incentives: IncentivesType = await this.request(
|
|
109
|
+
`/${DEFAULT_BASE_VERSION}/address/incentives/${address}`,
|
|
110
|
+
{
|
|
111
|
+
method: 'GET',
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
return incentives;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type ETH_ADDRESS } from './types';
|
|
2
|
+
import { Contract, type Signer } from 'ethers';
|
|
3
|
+
import { MerkleDistributorABI } from './abi/MerkleDistributorWithDeadline';
|
|
4
|
+
import { getProvider } from './utils';
|
|
5
|
+
|
|
6
|
+
export const claimIncentivesFromMerkleDistributor = async (incentivesData: {
|
|
7
|
+
signer: Signer;
|
|
8
|
+
contractAddress: ETH_ADDRESS;
|
|
9
|
+
index: number;
|
|
10
|
+
operatorAddress: ETH_ADDRESS;
|
|
11
|
+
amount: number;
|
|
12
|
+
merkleProof: string[];
|
|
13
|
+
}): Promise<{ txHash: string }> => {
|
|
14
|
+
try {
|
|
15
|
+
const contract = new Contract(
|
|
16
|
+
incentivesData.contractAddress,
|
|
17
|
+
MerkleDistributorABI.abi,
|
|
18
|
+
incentivesData.signer,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const tx = await contract.claim(
|
|
22
|
+
BigInt(incentivesData.index),
|
|
23
|
+
incentivesData.operatorAddress,
|
|
24
|
+
BigInt(incentivesData.amount),
|
|
25
|
+
incentivesData.merkleProof,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const receipt = await tx.wait();
|
|
29
|
+
|
|
30
|
+
return { txHash: receipt.hash };
|
|
31
|
+
} catch (error: any) {
|
|
32
|
+
console.log('Error claiming incentives:', error);
|
|
33
|
+
throw new Error(`Failed to claim incentives: ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const isClaimedFromMerkleDistributor = async (
|
|
38
|
+
chainId: number,
|
|
39
|
+
contractAddress: ETH_ADDRESS,
|
|
40
|
+
index: number,
|
|
41
|
+
): Promise<boolean> => {
|
|
42
|
+
try {
|
|
43
|
+
const provider = getProvider(chainId);
|
|
44
|
+
|
|
45
|
+
const contract = new Contract(
|
|
46
|
+
contractAddress,
|
|
47
|
+
MerkleDistributorABI.abi,
|
|
48
|
+
provider,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const claimed = await contract.isClaimed(BigInt(index));
|
|
52
|
+
|
|
53
|
+
return claimed;
|
|
54
|
+
} catch (error: any) {
|
|
55
|
+
console.log('Error checking claim status:', error);
|
|
56
|
+
throw new Error(`Failed to check claim status: ${error.message}`);
|
|
57
|
+
}
|
|
58
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
getOWRTranches,
|
|
48
48
|
} from './splitHelpers.js';
|
|
49
49
|
import { isContractAvailable } from './utils.js';
|
|
50
|
+
import { Incentives } from './incentives.js';
|
|
50
51
|
export * from './types.js';
|
|
51
52
|
export * from './services.js';
|
|
52
53
|
export * from './verification/signature-validator.js';
|
|
@@ -57,6 +58,7 @@ export * from './verification/common.js';
|
|
|
57
58
|
*/
|
|
58
59
|
export class Client extends Base {
|
|
59
60
|
private readonly signer: Signer | undefined;
|
|
61
|
+
public incentives: Incentives;
|
|
60
62
|
|
|
61
63
|
/**
|
|
62
64
|
* @param config - Client configurations
|
|
@@ -71,6 +73,11 @@ export class Client extends Base {
|
|
|
71
73
|
constructor(config: { baseUrl?: string; chainId?: number }, signer?: Signer) {
|
|
72
74
|
super(config);
|
|
73
75
|
this.signer = signer;
|
|
76
|
+
this.incentives = new Incentives(
|
|
77
|
+
this.signer,
|
|
78
|
+
this.chainId,
|
|
79
|
+
this.request.bind(this),
|
|
80
|
+
);
|
|
74
81
|
}
|
|
75
82
|
|
|
76
83
|
/**
|
package/src/types.ts
CHANGED
|
@@ -293,6 +293,26 @@ export type ClusterLock = {
|
|
|
293
293
|
node_signatures?: string[];
|
|
294
294
|
};
|
|
295
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Incentives
|
|
298
|
+
*/
|
|
299
|
+
export type Incentives = {
|
|
300
|
+
/** Operator Address. */
|
|
301
|
+
operator_address: string;
|
|
302
|
+
|
|
303
|
+
/** The amount the recipient is entitled to. */
|
|
304
|
+
amount: string;
|
|
305
|
+
|
|
306
|
+
/** The recipient’s index in the Merkle tree. */
|
|
307
|
+
index: number;
|
|
308
|
+
|
|
309
|
+
/** The Merkle proof (an array of hashes) generated for the recipient. */
|
|
310
|
+
merkle_proof: string[];
|
|
311
|
+
|
|
312
|
+
/** The MerkleDistributor contract address. */
|
|
313
|
+
contract_address: string;
|
|
314
|
+
};
|
|
315
|
+
|
|
296
316
|
/**
|
|
297
317
|
* String expected to be Ethereum Address
|
|
298
318
|
*/
|