genlayer-js 0.18.10 → 0.18.12
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/chains/index.cjs +2 -2
- package/dist/chains/index.js +1 -1
- package/dist/{chunk-PPBY3UXF.cjs → chunk-5TKVNHAO.cjs} +1118 -305
- package/dist/{chunk-WZNF2WK4.js → chunk-NOFMB7RP.js} +1118 -305
- package/dist/{index-D9ONjYgl.d.cts → index-DsN7LGHA.d.cts} +1615 -459
- package/dist/{index-ZDqJWXj0.d.ts → index-sw3NAvBf.d.ts} +1615 -459
- package/dist/index.cjs +58 -58
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +28 -28
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +11 -1
- package/.eslintignore +0 -2
- package/.eslintrc.cjs +0 -59
- package/.github/pull_request_template.md +0 -43
- package/.github/workflows/publish.yml +0 -41
- package/.github/workflows/test.yml +0 -33
- package/.prettierignore +0 -19
- package/.prettierrc +0 -12
- package/.release-it.json +0 -64
- package/CHANGELOG.md +0 -304
- package/CLAUDE.md +0 -66
- package/CONTRIBUTING.md +0 -87
- package/renovate.json +0 -20
- package/src/abi/calldata/consts.ts +0 -14
- package/src/abi/calldata/decoder.ts +0 -86
- package/src/abi/calldata/encoder.ts +0 -178
- package/src/abi/calldata/index.ts +0 -3
- package/src/abi/calldata/string.ts +0 -83
- package/src/abi/index.ts +0 -6
- package/src/abi/staking.ts +0 -687
- package/src/abi/transactions.ts +0 -11
- package/src/accounts/IAccountActions.ts +0 -5
- package/src/accounts/account.ts +0 -9
- package/src/accounts/actions.ts +0 -34
- package/src/chains/actions.ts +0 -40
- package/src/chains/index.ts +0 -4
- package/src/chains/localnet.ts +0 -4016
- package/src/chains/studionet.ts +0 -4017
- package/src/chains/testnetAsimov.ts +0 -4013
- package/src/client/client.ts +0 -139
- package/src/config/snapID.ts +0 -4
- package/src/config/transactions.ts +0 -9
- package/src/contracts/actions.ts +0 -387
- package/src/global.d.ts +0 -9
- package/src/index.ts +0 -12
- package/src/staking/actions.ts +0 -691
- package/src/staking/index.ts +0 -2
- package/src/staking/utils.ts +0 -22
- package/src/transactions/ITransactionActions.ts +0 -15
- package/src/transactions/actions.ts +0 -113
- package/src/transactions/decoders.ts +0 -275
- package/src/types/accounts.ts +0 -1
- package/src/types/calldata.ts +0 -31
- package/src/types/chains.ts +0 -22
- package/src/types/clients.ts +0 -106
- package/src/types/contracts.ts +0 -32
- package/src/types/index.ts +0 -9
- package/src/types/metamaskClientResult.ts +0 -5
- package/src/types/network.ts +0 -1
- package/src/types/snapSource.ts +0 -1
- package/src/types/staking.ts +0 -225
- package/src/types/transactions.ts +0 -312
- package/src/utils/async.ts +0 -3
- package/src/utils/jsonifier.ts +0 -119
- package/src/wallet/actions.ts +0 -10
- package/src/wallet/connect.ts +0 -67
- package/src/wallet/metamaskClient.ts +0 -50
- package/tests/client.test-d.ts +0 -67
- package/tests/client.test.ts +0 -197
- package/tests/smoke.test.ts +0 -59
- package/tests/transactions.test.ts +0 -142
- package/tsconfig.json +0 -119
- package/tsconfig.vitest-temp.json +0 -41
- package/vitest.config.ts +0 -18
package/src/staking/actions.ts
DELETED
|
@@ -1,691 +0,0 @@
|
|
|
1
|
-
import {getContract, decodeEventLog, PublicClient, Client, Transport, Chain, Account, Address as ViemAddress, GetContractReturnType, toHex, encodeFunctionData, BaseError, ContractFunctionRevertedError, decodeErrorResult, RawContractError} from "viem";
|
|
2
|
-
import {GenLayerClient, GenLayerChain, Address} from "@/types";
|
|
3
|
-
import {STAKING_ABI, VALIDATOR_WALLET_ABI} from "@/abi/staking";
|
|
4
|
-
import {parseStakingAmount, formatStakingAmount} from "./utils";
|
|
5
|
-
import {
|
|
6
|
-
ValidatorInfo,
|
|
7
|
-
ValidatorIdentity,
|
|
8
|
-
BannedValidatorInfo,
|
|
9
|
-
StakeInfo,
|
|
10
|
-
EpochInfo,
|
|
11
|
-
EpochData,
|
|
12
|
-
StakingTransactionResult,
|
|
13
|
-
ValidatorJoinResult,
|
|
14
|
-
DelegatorJoinResult,
|
|
15
|
-
ValidatorJoinOptions,
|
|
16
|
-
ValidatorDepositOptions,
|
|
17
|
-
ValidatorExitOptions,
|
|
18
|
-
ValidatorClaimOptions,
|
|
19
|
-
ValidatorPrimeOptions,
|
|
20
|
-
SetOperatorOptions,
|
|
21
|
-
SetIdentityOptions,
|
|
22
|
-
DelegatorJoinOptions,
|
|
23
|
-
DelegatorExitOptions,
|
|
24
|
-
DelegatorClaimOptions,
|
|
25
|
-
StakingContract,
|
|
26
|
-
PendingDeposit,
|
|
27
|
-
PendingWithdrawal,
|
|
28
|
-
} from "@/types/staking";
|
|
29
|
-
|
|
30
|
-
type ReadOnlyStakingContract = GetContractReturnType<typeof STAKING_ABI, PublicClient, ViemAddress>;
|
|
31
|
-
type WalletClientWithAccount = Client<Transport, Chain, Account>;
|
|
32
|
-
|
|
33
|
-
const FALLBACK_GAS = 1000000n;
|
|
34
|
-
const GAS_BUFFER_MULTIPLIER = 2n;
|
|
35
|
-
|
|
36
|
-
// Combined ABI for error decoding (both staking and validator wallet errors)
|
|
37
|
-
const COMBINED_ERROR_ABI = [...STAKING_ABI, ...VALIDATOR_WALLET_ABI];
|
|
38
|
-
|
|
39
|
-
function extractRevertReason(err: unknown): string {
|
|
40
|
-
if (err instanceof BaseError) {
|
|
41
|
-
// Try to find raw error data and decode it with our ABI
|
|
42
|
-
const rawError = err.walk((e) => e instanceof RawContractError);
|
|
43
|
-
if (rawError instanceof RawContractError && rawError.data && typeof rawError.data === "string") {
|
|
44
|
-
try {
|
|
45
|
-
const decoded = decodeErrorResult({
|
|
46
|
-
abi: COMBINED_ERROR_ABI,
|
|
47
|
-
data: rawError.data as `0x${string}`,
|
|
48
|
-
});
|
|
49
|
-
return decoded.errorName;
|
|
50
|
-
} catch {
|
|
51
|
-
// Fall through to other methods
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Try to extract error data from the cause chain
|
|
56
|
-
let current: unknown = err;
|
|
57
|
-
while (current) {
|
|
58
|
-
if (current && typeof current === "object") {
|
|
59
|
-
const obj = current as Record<string, unknown>;
|
|
60
|
-
// Check for data property that looks like hex error data
|
|
61
|
-
if (obj.data && typeof obj.data === "string" && obj.data.startsWith("0x")) {
|
|
62
|
-
try {
|
|
63
|
-
const decoded = decodeErrorResult({
|
|
64
|
-
abi: COMBINED_ERROR_ABI,
|
|
65
|
-
data: obj.data as `0x${string}`,
|
|
66
|
-
});
|
|
67
|
-
return decoded.errorName;
|
|
68
|
-
} catch {
|
|
69
|
-
// Continue searching
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
current = obj.cause;
|
|
73
|
-
} else {
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const revertError = err.walk((e) => e instanceof ContractFunctionRevertedError);
|
|
79
|
-
if (revertError instanceof ContractFunctionRevertedError) {
|
|
80
|
-
// If viem already decoded it, use that
|
|
81
|
-
if (revertError.data?.errorName) {
|
|
82
|
-
return revertError.data.errorName;
|
|
83
|
-
}
|
|
84
|
-
return revertError.reason || "Unknown reason";
|
|
85
|
-
}
|
|
86
|
-
if (err.shortMessage) return err.shortMessage;
|
|
87
|
-
}
|
|
88
|
-
if (err instanceof Error) return err.message;
|
|
89
|
-
return "Unknown reason";
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export const stakingActions = (
|
|
93
|
-
client: GenLayerClient<GenLayerChain>,
|
|
94
|
-
publicClient: PublicClient,
|
|
95
|
-
) => {
|
|
96
|
-
const executeWrite = async (options: {
|
|
97
|
-
to: ViemAddress;
|
|
98
|
-
data: `0x${string}`;
|
|
99
|
-
value?: bigint;
|
|
100
|
-
gas?: bigint;
|
|
101
|
-
}): Promise<StakingTransactionResult> => {
|
|
102
|
-
if (!client.account) {
|
|
103
|
-
throw new Error("Account is required for write operations. Initialize client with a wallet account.");
|
|
104
|
-
}
|
|
105
|
-
const account = client.account;
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
await publicClient.call({
|
|
109
|
-
account,
|
|
110
|
-
to: options.to,
|
|
111
|
-
data: options.data,
|
|
112
|
-
value: options.value,
|
|
113
|
-
});
|
|
114
|
-
} catch (err: unknown) {
|
|
115
|
-
const revertReason = extractRevertReason(err);
|
|
116
|
-
throw new Error(`Transaction would revert: ${revertReason}`);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
let gasLimit = options.gas;
|
|
120
|
-
if (!gasLimit) {
|
|
121
|
-
try {
|
|
122
|
-
const estimated = await publicClient.estimateGas({
|
|
123
|
-
account,
|
|
124
|
-
to: options.to,
|
|
125
|
-
data: options.data,
|
|
126
|
-
value: options.value,
|
|
127
|
-
});
|
|
128
|
-
gasLimit = estimated * GAS_BUFFER_MULTIPLIER;
|
|
129
|
-
} catch {
|
|
130
|
-
gasLimit = FALLBACK_GAS;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const nonce = await publicClient.getTransactionCount({address: account.address as ViemAddress});
|
|
135
|
-
|
|
136
|
-
const txRequest = await publicClient.prepareTransactionRequest({
|
|
137
|
-
account,
|
|
138
|
-
to: options.to,
|
|
139
|
-
data: options.data,
|
|
140
|
-
value: options.value,
|
|
141
|
-
type: "legacy",
|
|
142
|
-
nonce,
|
|
143
|
-
gas: gasLimit,
|
|
144
|
-
chain: client.chain,
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const signTransaction = account.signTransaction;
|
|
148
|
-
if (!signTransaction) {
|
|
149
|
-
throw new Error("Account does not support signing transactions");
|
|
150
|
-
}
|
|
151
|
-
const serializedTx = await signTransaction(txRequest as Parameters<typeof signTransaction>[0]);
|
|
152
|
-
const hash = await publicClient.sendRawTransaction({serializedTransaction: serializedTx});
|
|
153
|
-
const receipt = await publicClient.waitForTransactionReceipt({hash});
|
|
154
|
-
|
|
155
|
-
if (receipt.status === "reverted") {
|
|
156
|
-
let revertReason = "Unknown reason";
|
|
157
|
-
try {
|
|
158
|
-
await publicClient.call({
|
|
159
|
-
account,
|
|
160
|
-
to: options.to,
|
|
161
|
-
data: options.data,
|
|
162
|
-
value: options.value,
|
|
163
|
-
blockNumber: receipt.blockNumber,
|
|
164
|
-
});
|
|
165
|
-
const gasUsed = receipt.gasUsed;
|
|
166
|
-
if (gasUsed >= gasLimit - 1000n) {
|
|
167
|
-
revertReason = `Out of gas (used ${gasUsed}, limit ${gasLimit})`;
|
|
168
|
-
} else {
|
|
169
|
-
revertReason = `Unknown (simulation passes but tx reverts). Gas: ${gasUsed}/${gasLimit}`;
|
|
170
|
-
}
|
|
171
|
-
} catch (err: unknown) {
|
|
172
|
-
revertReason = extractRevertReason(err);
|
|
173
|
-
}
|
|
174
|
-
throw new Error(`Transaction reverted: ${revertReason} (tx: ${hash})`);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
transactionHash: receipt.transactionHash,
|
|
179
|
-
blockNumber: receipt.blockNumber,
|
|
180
|
-
gasUsed: receipt.gasUsed,
|
|
181
|
-
};
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
const getStakingAddress = (): ViemAddress => {
|
|
185
|
-
const stakingConfig = client.chain.stakingContract;
|
|
186
|
-
if (!stakingConfig?.address || stakingConfig.address === "0x0000000000000000000000000000000000000000") {
|
|
187
|
-
throw new Error("Staking is not supported on studio-based networks. Use testnet-asimov for staking operations.");
|
|
188
|
-
}
|
|
189
|
-
return stakingConfig.address as ViemAddress;
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const getStakingContract = (): StakingContract => {
|
|
193
|
-
const address = getStakingAddress();
|
|
194
|
-
return getContract({
|
|
195
|
-
address,
|
|
196
|
-
abi: STAKING_ABI,
|
|
197
|
-
client: {public: publicClient, wallet: client as unknown as WalletClientWithAccount},
|
|
198
|
-
});
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
const getReadOnlyStakingContract = (): ReadOnlyStakingContract => {
|
|
202
|
-
const address = getStakingAddress();
|
|
203
|
-
return getContract({
|
|
204
|
-
address,
|
|
205
|
-
abi: STAKING_ABI,
|
|
206
|
-
client: publicClient,
|
|
207
|
-
});
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
validatorJoin: async (options: ValidatorJoinOptions): Promise<ValidatorJoinResult> => {
|
|
212
|
-
const amount = parseStakingAmount(options.amount);
|
|
213
|
-
const stakingAddress = getStakingAddress();
|
|
214
|
-
|
|
215
|
-
const data = options.operator
|
|
216
|
-
? encodeFunctionData({
|
|
217
|
-
abi: STAKING_ABI,
|
|
218
|
-
functionName: "validatorJoin",
|
|
219
|
-
args: [options.operator as ViemAddress],
|
|
220
|
-
})
|
|
221
|
-
: encodeFunctionData({
|
|
222
|
-
abi: STAKING_ABI,
|
|
223
|
-
functionName: "validatorJoin",
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
const result = await executeWrite({to: stakingAddress, data, value: amount});
|
|
227
|
-
const receipt = await publicClient.getTransactionReceipt({hash: result.transactionHash});
|
|
228
|
-
|
|
229
|
-
let validatorWallet: Address | undefined;
|
|
230
|
-
let eventFound = false;
|
|
231
|
-
for (const log of receipt.logs) {
|
|
232
|
-
try {
|
|
233
|
-
const decoded = decodeEventLog({abi: STAKING_ABI, data: log.data, topics: log.topics});
|
|
234
|
-
if (decoded.eventName === "ValidatorJoin") {
|
|
235
|
-
validatorWallet = (decoded.args as {validator: Address}).validator;
|
|
236
|
-
eventFound = true;
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
} catch {
|
|
240
|
-
// Not a ValidatorJoin event - continue searching
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (!eventFound) {
|
|
245
|
-
throw new Error(
|
|
246
|
-
`ValidatorJoin event not found in transaction ${result.transactionHash}. ` +
|
|
247
|
-
`Transaction succeeded but validator wallet address could not be determined.`,
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return {
|
|
252
|
-
transactionHash: receipt.transactionHash,
|
|
253
|
-
blockNumber: receipt.blockNumber,
|
|
254
|
-
gasUsed: receipt.gasUsed,
|
|
255
|
-
validatorWallet: validatorWallet!,
|
|
256
|
-
operator: options.operator || (client.account!.address as Address),
|
|
257
|
-
amount: formatStakingAmount(amount),
|
|
258
|
-
amountRaw: amount,
|
|
259
|
-
};
|
|
260
|
-
},
|
|
261
|
-
|
|
262
|
-
validatorDeposit: async (options: ValidatorDepositOptions): Promise<StakingTransactionResult> => {
|
|
263
|
-
const amount = parseStakingAmount(options.amount);
|
|
264
|
-
const data = encodeFunctionData({
|
|
265
|
-
abi: STAKING_ABI,
|
|
266
|
-
functionName: "validatorDeposit",
|
|
267
|
-
});
|
|
268
|
-
return executeWrite({to: getStakingAddress(), data, value: amount});
|
|
269
|
-
},
|
|
270
|
-
|
|
271
|
-
validatorExit: async (options: ValidatorExitOptions): Promise<StakingTransactionResult> => {
|
|
272
|
-
const shares = typeof options.shares === "string" ? BigInt(options.shares) : options.shares;
|
|
273
|
-
const data = encodeFunctionData({
|
|
274
|
-
abi: STAKING_ABI,
|
|
275
|
-
functionName: "validatorExit",
|
|
276
|
-
args: [shares],
|
|
277
|
-
});
|
|
278
|
-
return executeWrite({to: getStakingAddress(), data});
|
|
279
|
-
},
|
|
280
|
-
|
|
281
|
-
validatorClaim: async (options?: ValidatorClaimOptions): Promise<StakingTransactionResult & {claimedAmount: bigint}> => {
|
|
282
|
-
if (!options?.validator && !client.account) {
|
|
283
|
-
throw new Error("Either provide validator address or initialize client with an account");
|
|
284
|
-
}
|
|
285
|
-
const validatorAddress = options?.validator || (client.account!.address as Address);
|
|
286
|
-
const data = encodeFunctionData({
|
|
287
|
-
abi: STAKING_ABI,
|
|
288
|
-
functionName: "validatorClaim",
|
|
289
|
-
args: [validatorAddress as ViemAddress],
|
|
290
|
-
});
|
|
291
|
-
const result = await executeWrite({to: getStakingAddress(), data});
|
|
292
|
-
// TODO: Parse ClaimAmount from logs if needed
|
|
293
|
-
return {...result, claimedAmount: 0n};
|
|
294
|
-
},
|
|
295
|
-
|
|
296
|
-
validatorPrime: async (options: ValidatorPrimeOptions): Promise<StakingTransactionResult> => {
|
|
297
|
-
const data = encodeFunctionData({
|
|
298
|
-
abi: STAKING_ABI,
|
|
299
|
-
functionName: "validatorPrime",
|
|
300
|
-
args: [options.validator as ViemAddress],
|
|
301
|
-
});
|
|
302
|
-
return executeWrite({to: getStakingAddress(), data});
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
setOperator: async (options: SetOperatorOptions): Promise<StakingTransactionResult> => {
|
|
306
|
-
const data = encodeFunctionData({
|
|
307
|
-
abi: VALIDATOR_WALLET_ABI,
|
|
308
|
-
functionName: "setOperator",
|
|
309
|
-
args: [options.operator as ViemAddress],
|
|
310
|
-
});
|
|
311
|
-
return executeWrite({to: options.validator as ViemAddress, data});
|
|
312
|
-
},
|
|
313
|
-
|
|
314
|
-
setIdentity: async (options: SetIdentityOptions): Promise<StakingTransactionResult> => {
|
|
315
|
-
let extraCidBytes: `0x${string}` = "0x";
|
|
316
|
-
if (options.extraCid) {
|
|
317
|
-
if (options.extraCid.startsWith("0x")) {
|
|
318
|
-
extraCidBytes = options.extraCid as `0x${string}`;
|
|
319
|
-
} else {
|
|
320
|
-
extraCidBytes = toHex(new TextEncoder().encode(options.extraCid));
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
const data = encodeFunctionData({
|
|
324
|
-
abi: VALIDATOR_WALLET_ABI,
|
|
325
|
-
functionName: "setIdentity",
|
|
326
|
-
args: [
|
|
327
|
-
options.moniker,
|
|
328
|
-
options.logoUri || "",
|
|
329
|
-
options.website || "",
|
|
330
|
-
options.description || "",
|
|
331
|
-
options.email || "",
|
|
332
|
-
options.twitter || "",
|
|
333
|
-
options.telegram || "",
|
|
334
|
-
options.github || "",
|
|
335
|
-
extraCidBytes,
|
|
336
|
-
],
|
|
337
|
-
});
|
|
338
|
-
return executeWrite({to: options.validator as ViemAddress, data});
|
|
339
|
-
},
|
|
340
|
-
|
|
341
|
-
delegatorJoin: async (options: DelegatorJoinOptions): Promise<DelegatorJoinResult> => {
|
|
342
|
-
const amount = parseStakingAmount(options.amount);
|
|
343
|
-
const data = encodeFunctionData({
|
|
344
|
-
abi: STAKING_ABI,
|
|
345
|
-
functionName: "delegatorJoin",
|
|
346
|
-
args: [options.validator as ViemAddress],
|
|
347
|
-
});
|
|
348
|
-
const result = await executeWrite({to: getStakingAddress(), data, value: amount});
|
|
349
|
-
|
|
350
|
-
return {
|
|
351
|
-
...result,
|
|
352
|
-
validator: options.validator,
|
|
353
|
-
delegator: client.account!.address as Address,
|
|
354
|
-
amount: formatStakingAmount(amount),
|
|
355
|
-
amountRaw: amount,
|
|
356
|
-
};
|
|
357
|
-
},
|
|
358
|
-
|
|
359
|
-
delegatorExit: async (options: DelegatorExitOptions): Promise<StakingTransactionResult> => {
|
|
360
|
-
const shares = typeof options.shares === "string" ? BigInt(options.shares) : options.shares;
|
|
361
|
-
const data = encodeFunctionData({
|
|
362
|
-
abi: STAKING_ABI,
|
|
363
|
-
functionName: "delegatorExit",
|
|
364
|
-
args: [options.validator as ViemAddress, shares],
|
|
365
|
-
});
|
|
366
|
-
return executeWrite({to: getStakingAddress(), data});
|
|
367
|
-
},
|
|
368
|
-
|
|
369
|
-
delegatorClaim: async (options: DelegatorClaimOptions): Promise<StakingTransactionResult> => {
|
|
370
|
-
if (!options.delegator && !client.account) {
|
|
371
|
-
throw new Error("Either provide delegator address or initialize client with an account");
|
|
372
|
-
}
|
|
373
|
-
const delegatorAddress = options.delegator || (client.account!.address as Address);
|
|
374
|
-
const data = encodeFunctionData({
|
|
375
|
-
abi: STAKING_ABI,
|
|
376
|
-
functionName: "delegatorClaim",
|
|
377
|
-
args: [delegatorAddress as ViemAddress, options.validator as ViemAddress],
|
|
378
|
-
});
|
|
379
|
-
return executeWrite({to: getStakingAddress(), data});
|
|
380
|
-
},
|
|
381
|
-
|
|
382
|
-
isValidator: async (address: Address): Promise<boolean> => {
|
|
383
|
-
const contract = getReadOnlyStakingContract();
|
|
384
|
-
return contract.read.isValidator([address as ViemAddress]) as Promise<boolean>;
|
|
385
|
-
},
|
|
386
|
-
|
|
387
|
-
getValidatorInfo: async (validator: Address): Promise<ValidatorInfo> => {
|
|
388
|
-
const contract = getReadOnlyStakingContract();
|
|
389
|
-
|
|
390
|
-
const isVal = await contract.read.isValidator([validator as ViemAddress]);
|
|
391
|
-
if (!isVal) {
|
|
392
|
-
throw new Error(`Address ${validator} is not a validator`);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// Get validator wallet contract for owner/operator/identity
|
|
396
|
-
const walletContract = getContract({
|
|
397
|
-
address: validator as ViemAddress,
|
|
398
|
-
abi: VALIDATOR_WALLET_ABI,
|
|
399
|
-
client: publicClient,
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
// Fetch all data in parallel
|
|
403
|
-
const [view, owner, operator, identityRaw, currentEpoch] = await Promise.all([
|
|
404
|
-
contract.read.validatorView([validator as ViemAddress]) as Promise<any>,
|
|
405
|
-
walletContract.read.owner() as Promise<Address>,
|
|
406
|
-
walletContract.read.operator() as Promise<Address>,
|
|
407
|
-
walletContract.read.getIdentity().catch(() => null) as Promise<any>,
|
|
408
|
-
contract.read.epoch() as Promise<bigint>,
|
|
409
|
-
]);
|
|
410
|
-
|
|
411
|
-
// Parse identity if available
|
|
412
|
-
let identity: ValidatorIdentity | undefined;
|
|
413
|
-
if (identityRaw && identityRaw.moniker) {
|
|
414
|
-
identity = {
|
|
415
|
-
moniker: identityRaw.moniker,
|
|
416
|
-
logoUri: identityRaw.logoUri,
|
|
417
|
-
website: identityRaw.website,
|
|
418
|
-
description: identityRaw.description,
|
|
419
|
-
email: identityRaw.email,
|
|
420
|
-
twitter: identityRaw.twitter,
|
|
421
|
-
telegram: identityRaw.telegram,
|
|
422
|
-
github: identityRaw.github,
|
|
423
|
-
extraCid: identityRaw.extraCid ? toHex(identityRaw.extraCid) : "",
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Validator needs priming if ePrimed < currentEpoch - 1
|
|
428
|
-
const needsPriming = currentEpoch > 0n && view.ePrimed < currentEpoch - 1n;
|
|
429
|
-
|
|
430
|
-
// Fetch pending self-stake deposits
|
|
431
|
-
const depositLen = (await contract.read.validatorDepositLen([validator as ViemAddress])) as bigint;
|
|
432
|
-
const pendingDeposits: PendingDeposit[] = [];
|
|
433
|
-
|
|
434
|
-
for (let i = 0n; i < depositLen; i++) {
|
|
435
|
-
const [epoch, commit] = (await contract.read.validatorDeposit([validator as ViemAddress, i])) as [
|
|
436
|
-
bigint,
|
|
437
|
-
{input: bigint; output: bigint; epoch: bigint; linkToNextCommit: bigint},
|
|
438
|
-
];
|
|
439
|
-
pendingDeposits.push({
|
|
440
|
-
epoch,
|
|
441
|
-
stake: formatStakingAmount(commit.input),
|
|
442
|
-
stakeRaw: commit.input,
|
|
443
|
-
shares: commit.output,
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Fetch pending self-stake withdrawals
|
|
448
|
-
const withdrawalLen = (await contract.read.validatorWithdrawalLen([validator as ViemAddress])) as bigint;
|
|
449
|
-
const pendingWithdrawals: PendingWithdrawal[] = [];
|
|
450
|
-
|
|
451
|
-
for (let i = 0n; i < withdrawalLen; i++) {
|
|
452
|
-
const [epoch, commit] = (await contract.read.validatorWithdrawal([validator as ViemAddress, i])) as [
|
|
453
|
-
bigint,
|
|
454
|
-
{input: bigint; output: bigint; epoch: bigint; linkToNextCommit: bigint},
|
|
455
|
-
];
|
|
456
|
-
pendingWithdrawals.push({
|
|
457
|
-
epoch,
|
|
458
|
-
shares: commit.input,
|
|
459
|
-
stake: formatStakingAmount(commit.output),
|
|
460
|
-
stakeRaw: commit.output,
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return {
|
|
465
|
-
address: validator,
|
|
466
|
-
owner,
|
|
467
|
-
operator,
|
|
468
|
-
vStake: formatStakingAmount(view.vStake),
|
|
469
|
-
vStakeRaw: view.vStake,
|
|
470
|
-
vShares: view.vShares,
|
|
471
|
-
dStake: formatStakingAmount(view.dStake),
|
|
472
|
-
dStakeRaw: view.dStake,
|
|
473
|
-
dShares: view.dShares,
|
|
474
|
-
vDeposit: formatStakingAmount(view.vDeposit),
|
|
475
|
-
vDepositRaw: view.vDeposit,
|
|
476
|
-
vWithdrawal: formatStakingAmount(view.vWithdrawal),
|
|
477
|
-
vWithdrawalRaw: view.vWithdrawal,
|
|
478
|
-
ePrimed: view.ePrimed,
|
|
479
|
-
live: view.live,
|
|
480
|
-
banned: view.eBanned > 0n,
|
|
481
|
-
bannedEpoch: view.eBanned > 0n ? view.eBanned : undefined,
|
|
482
|
-
needsPriming,
|
|
483
|
-
identity,
|
|
484
|
-
pendingDeposits,
|
|
485
|
-
pendingWithdrawals,
|
|
486
|
-
};
|
|
487
|
-
},
|
|
488
|
-
|
|
489
|
-
getStakeInfo: async (delegator: Address, validator: Address): Promise<StakeInfo> => {
|
|
490
|
-
const contract = getReadOnlyStakingContract();
|
|
491
|
-
|
|
492
|
-
const shares = (await contract.read.sharesOf([delegator as ViemAddress, validator as ViemAddress])) as bigint;
|
|
493
|
-
// stakeOf divides by shares, so it fails with division by zero if no shares yet
|
|
494
|
-
let stake = 0n;
|
|
495
|
-
if (shares > 0n) {
|
|
496
|
-
stake = (await contract.read.stakeOf([delegator as ViemAddress, validator as ViemAddress])) as bigint;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Fetch pending delegator deposits
|
|
500
|
-
const depositLen = (await contract.read.delegatorDepositLen([
|
|
501
|
-
delegator as ViemAddress,
|
|
502
|
-
validator as ViemAddress,
|
|
503
|
-
])) as bigint;
|
|
504
|
-
const pendingDeposits: PendingDeposit[] = [];
|
|
505
|
-
|
|
506
|
-
for (let i = 0n; i < depositLen; i++) {
|
|
507
|
-
const [claim, commit] = (await contract.read.delegatorDeposit([
|
|
508
|
-
delegator as ViemAddress,
|
|
509
|
-
validator as ViemAddress,
|
|
510
|
-
i,
|
|
511
|
-
])) as [
|
|
512
|
-
{quantity: bigint; commit: bigint},
|
|
513
|
-
{input: bigint; output: bigint; epoch: bigint; linkToNextCommit: bigint},
|
|
514
|
-
];
|
|
515
|
-
pendingDeposits.push({
|
|
516
|
-
epoch: commit.epoch,
|
|
517
|
-
stake: formatStakingAmount(commit.input),
|
|
518
|
-
stakeRaw: commit.input,
|
|
519
|
-
shares: claim.quantity,
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// Fetch pending delegator withdrawals
|
|
524
|
-
const withdrawalLen = (await contract.read.delegatorWithdrawalLen([
|
|
525
|
-
delegator as ViemAddress,
|
|
526
|
-
validator as ViemAddress,
|
|
527
|
-
])) as bigint;
|
|
528
|
-
const pendingWithdrawals: PendingWithdrawal[] = [];
|
|
529
|
-
|
|
530
|
-
for (let i = 0n; i < withdrawalLen; i++) {
|
|
531
|
-
const [claim, commit] = (await contract.read.delegatorWithdrawal([
|
|
532
|
-
delegator as ViemAddress,
|
|
533
|
-
validator as ViemAddress,
|
|
534
|
-
i,
|
|
535
|
-
])) as [
|
|
536
|
-
{quantity: bigint; commit: bigint},
|
|
537
|
-
{input: bigint; output: bigint; epoch: bigint; linkToNextCommit: bigint},
|
|
538
|
-
];
|
|
539
|
-
pendingWithdrawals.push({
|
|
540
|
-
epoch: commit.epoch,
|
|
541
|
-
shares: claim.quantity,
|
|
542
|
-
stake: formatStakingAmount(commit.output),
|
|
543
|
-
stakeRaw: commit.output,
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
return {
|
|
548
|
-
delegator,
|
|
549
|
-
validator,
|
|
550
|
-
shares,
|
|
551
|
-
stake: formatStakingAmount(stake),
|
|
552
|
-
stakeRaw: stake,
|
|
553
|
-
pendingDeposits,
|
|
554
|
-
pendingWithdrawals,
|
|
555
|
-
};
|
|
556
|
-
},
|
|
557
|
-
|
|
558
|
-
getEpochInfo: async (): Promise<EpochInfo> => {
|
|
559
|
-
const contract = getReadOnlyStakingContract();
|
|
560
|
-
|
|
561
|
-
const [
|
|
562
|
-
epoch,
|
|
563
|
-
finalized,
|
|
564
|
-
validatorMinStake,
|
|
565
|
-
delegatorMinStake,
|
|
566
|
-
activeCount,
|
|
567
|
-
epochMinDuration,
|
|
568
|
-
epochZeroMinDuration,
|
|
569
|
-
epochOdd,
|
|
570
|
-
epochEven,
|
|
571
|
-
] = await Promise.all([
|
|
572
|
-
contract.read.epoch() as Promise<bigint>,
|
|
573
|
-
contract.read.finalized() as Promise<bigint>,
|
|
574
|
-
contract.read.validatorMinStake() as Promise<bigint>,
|
|
575
|
-
contract.read.delegatorMinStake() as Promise<bigint>,
|
|
576
|
-
contract.read.activeValidatorsCount() as Promise<bigint>,
|
|
577
|
-
contract.read.epochMinDuration() as Promise<bigint>,
|
|
578
|
-
contract.read.epochZeroMinDuration() as Promise<bigint>,
|
|
579
|
-
contract.read.epochOdd() as Promise<any>,
|
|
580
|
-
contract.read.epochEven() as Promise<any>,
|
|
581
|
-
]);
|
|
582
|
-
|
|
583
|
-
// Current epoch data for next epoch estimate
|
|
584
|
-
const currentEpochData = epoch % 2n === 0n ? epochEven : epochOdd;
|
|
585
|
-
const currentEpochEnd = currentEpochData.end > 0n;
|
|
586
|
-
|
|
587
|
-
// Estimate next epoch: current start + min duration (if epoch hasn't ended)
|
|
588
|
-
let nextEpochEstimate: Date | null = null;
|
|
589
|
-
if (!currentEpochEnd) {
|
|
590
|
-
const duration = epoch === 0n ? epochZeroMinDuration : epochMinDuration;
|
|
591
|
-
const estimatedEndMs = Number(currentEpochData.start + duration) * 1000;
|
|
592
|
-
nextEpochEstimate = new Date(estimatedEndMs);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
return {
|
|
596
|
-
currentEpoch: epoch,
|
|
597
|
-
lastFinalizedEpoch: finalized,
|
|
598
|
-
validatorMinStake: formatStakingAmount(validatorMinStake),
|
|
599
|
-
validatorMinStakeRaw: validatorMinStake,
|
|
600
|
-
delegatorMinStake: formatStakingAmount(delegatorMinStake),
|
|
601
|
-
delegatorMinStakeRaw: delegatorMinStake,
|
|
602
|
-
activeValidatorsCount: activeCount,
|
|
603
|
-
epochMinDuration,
|
|
604
|
-
nextEpochEstimate,
|
|
605
|
-
};
|
|
606
|
-
},
|
|
607
|
-
|
|
608
|
-
getEpochData: async (epochNumber: bigint): Promise<EpochData> => {
|
|
609
|
-
const contract = getReadOnlyStakingContract();
|
|
610
|
-
|
|
611
|
-
const [currentEpoch, epochOdd, epochEven] = await Promise.all([
|
|
612
|
-
contract.read.epoch() as Promise<bigint>,
|
|
613
|
-
contract.read.epochOdd() as Promise<any>,
|
|
614
|
-
contract.read.epochEven() as Promise<any>,
|
|
615
|
-
]);
|
|
616
|
-
|
|
617
|
-
// Epochs alternate between odd/even storage slots
|
|
618
|
-
// Current epoch N uses: N % 2 === 0 ? epochEven : epochOdd
|
|
619
|
-
// We can only access current epoch and previous epoch (N-1)
|
|
620
|
-
if (epochNumber > currentEpoch) {
|
|
621
|
-
throw new Error(`Epoch ${epochNumber} has not started yet (current: ${currentEpoch})`);
|
|
622
|
-
}
|
|
623
|
-
if (epochNumber < currentEpoch - 1n && currentEpoch > 0n) {
|
|
624
|
-
throw new Error(`Epoch ${epochNumber} data no longer available (only current and previous epoch stored)`);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
const epochData = epochNumber % 2n === 0n ? epochEven : epochOdd;
|
|
628
|
-
|
|
629
|
-
return {
|
|
630
|
-
start: epochData.start,
|
|
631
|
-
end: epochData.end,
|
|
632
|
-
inflation: epochData.inflation,
|
|
633
|
-
weight: epochData.weight,
|
|
634
|
-
weightDeposit: epochData.weightDeposit,
|
|
635
|
-
weightWithdrawal: epochData.weightWithdrawal,
|
|
636
|
-
vcount: epochData.vcount,
|
|
637
|
-
claimed: epochData.claimed,
|
|
638
|
-
stakeDeposit: epochData.stakeDeposit,
|
|
639
|
-
stakeWithdrawal: epochData.stakeWithdrawal,
|
|
640
|
-
slashed: epochData.slashed,
|
|
641
|
-
};
|
|
642
|
-
},
|
|
643
|
-
|
|
644
|
-
getActiveValidators: async (): Promise<Address[]> => {
|
|
645
|
-
const contract = getReadOnlyStakingContract();
|
|
646
|
-
const validators = (await contract.read.activeValidators()) as Address[];
|
|
647
|
-
return validators.filter(v => v !== "0x0000000000000000000000000000000000000000");
|
|
648
|
-
},
|
|
649
|
-
|
|
650
|
-
getActiveValidatorsCount: async (): Promise<bigint> => {
|
|
651
|
-
const contract = getReadOnlyStakingContract();
|
|
652
|
-
return contract.read.activeValidatorsCount() as Promise<bigint>;
|
|
653
|
-
},
|
|
654
|
-
|
|
655
|
-
getQuarantinedValidators: async (): Promise<Address[]> => {
|
|
656
|
-
const contract = getReadOnlyStakingContract();
|
|
657
|
-
return contract.read.getQuarantinedValidators() as Promise<Address[]>;
|
|
658
|
-
},
|
|
659
|
-
|
|
660
|
-
getBannedValidators: async (startIndex = 0n, size = 100n): Promise<BannedValidatorInfo[]> => {
|
|
661
|
-
const contract = getReadOnlyStakingContract();
|
|
662
|
-
const result = (await contract.read.getAllBannedValidators([startIndex, size])) as any[];
|
|
663
|
-
return result.map((v: any) => ({
|
|
664
|
-
validator: v.validator as Address,
|
|
665
|
-
untilEpoch: v.untilEpochBanned,
|
|
666
|
-
permanentlyBanned: v.permanentlyBanned,
|
|
667
|
-
}));
|
|
668
|
-
},
|
|
669
|
-
|
|
670
|
-
getQuarantinedValidatorsDetailed: async (startIndex = 0n, size = 100n): Promise<BannedValidatorInfo[]> => {
|
|
671
|
-
const contract = getReadOnlyStakingContract();
|
|
672
|
-
const result = (await contract.read.getAllQuarantinedValidators([startIndex, size])) as any[];
|
|
673
|
-
return result.map((v: any) => ({
|
|
674
|
-
validator: v.validator as Address,
|
|
675
|
-
untilEpoch: v.untilEpochBanned,
|
|
676
|
-
permanentlyBanned: v.permanentlyBanned,
|
|
677
|
-
}));
|
|
678
|
-
},
|
|
679
|
-
|
|
680
|
-
getSlashingAddress: async (): Promise<Address> => {
|
|
681
|
-
const contract = getReadOnlyStakingContract();
|
|
682
|
-
// contracts() returns tuple: [gen, transactions, idleness, tribunal, slashing, consensus, validatorWalletFactory, nftMinter]
|
|
683
|
-
const externalContracts = (await contract.read.contracts()) as readonly ViemAddress[];
|
|
684
|
-
return externalContracts[4] as Address; // slashing is at index 4
|
|
685
|
-
},
|
|
686
|
-
|
|
687
|
-
getStakingContract,
|
|
688
|
-
parseStakingAmount,
|
|
689
|
-
formatStakingAmount,
|
|
690
|
-
};
|
|
691
|
-
};
|
package/src/staking/index.ts
DELETED