genlayer-js 0.18.9 → 0.18.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.
- package/.eslintignore +2 -1
- package/.github/workflows/publish.yml +73 -16
- package/.github/workflows/smoke.yml +27 -0
- package/.release-it.json +3 -1
- package/CHANGELOG.md +4 -0
- package/dist/chains/index.cjs +2 -2
- package/dist/chains/index.js +1 -1
- package/dist/{chunk-SC2VXHL3.cjs → chunk-5TKVNHAO.cjs} +1124 -309
- package/dist/{chunk-V3MYVW3P.js → chunk-NOFMB7RP.js} +1124 -309
- 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 +6 -1
- package/src/abi/staking.ts +1111 -297
- package/src/chains/testnetAsimov.ts +6 -4
- package/src/staking/actions.ts +29 -30
- package/src/types/staking.ts +0 -5
- package/tests/smoke.test.ts +291 -0
- package/vitest.config.ts +1 -0
- package/vitest.smoke.config.ts +16 -0
|
@@ -3,15 +3,16 @@ import {GenLayerChain} from "@/types";
|
|
|
3
3
|
import {STAKING_ABI} from "@/abi/staking";
|
|
4
4
|
|
|
5
5
|
// chains/localnet.ts
|
|
6
|
-
const TESTNET_JSON_RPC_URL = "https://
|
|
6
|
+
const TESTNET_JSON_RPC_URL = "https://zksync-os-testnet-genlayer.zksync.dev";
|
|
7
|
+
const TESTNET_WS_URL = "wss://zksync-os-testnet-genlayer.zksync.dev/ws";
|
|
7
8
|
|
|
8
9
|
const STAKING_CONTRACT = {
|
|
9
|
-
address: "
|
|
10
|
+
address: "0x63Fa5E0bb10fb6fA98F44726C5518223F767687A" as Address,
|
|
10
11
|
abi: STAKING_ABI,
|
|
11
12
|
};
|
|
12
13
|
const EXPLORER_URL = "https://explorer-asimov.genlayer.com/";
|
|
13
14
|
const CONSENSUS_MAIN_CONTRACT = {
|
|
14
|
-
address: "
|
|
15
|
+
address: "0x6CAFF6769d70824745AD895663409DC70aB5B28E" as Address,
|
|
15
16
|
abi: [
|
|
16
17
|
{
|
|
17
18
|
inputs: [],
|
|
@@ -1401,7 +1402,7 @@ const CONSENSUS_MAIN_CONTRACT = {
|
|
|
1401
1402
|
};
|
|
1402
1403
|
|
|
1403
1404
|
const CONSENSUS_DATA_CONTRACT = {
|
|
1404
|
-
address: "
|
|
1405
|
+
address: "0x0D9d1d74d72Fa5eB94bcf746C8FCcb312a722c9B" as Address,
|
|
1405
1406
|
abi: [
|
|
1406
1407
|
{
|
|
1407
1408
|
inputs: [],
|
|
@@ -3989,6 +3990,7 @@ export const testnetAsimov: GenLayerChain = defineChain({
|
|
|
3989
3990
|
rpcUrls: {
|
|
3990
3991
|
default: {
|
|
3991
3992
|
http: [TESTNET_JSON_RPC_URL],
|
|
3993
|
+
webSocket: [TESTNET_WS_URL],
|
|
3992
3994
|
},
|
|
3993
3995
|
},
|
|
3994
3996
|
nativeCurrency: {
|
package/src/staking/actions.ts
CHANGED
|
@@ -561,8 +561,6 @@ export const stakingActions = (
|
|
|
561
561
|
const [
|
|
562
562
|
epoch,
|
|
563
563
|
finalized,
|
|
564
|
-
validatorMinStake,
|
|
565
|
-
delegatorMinStake,
|
|
566
564
|
activeCount,
|
|
567
565
|
epochMinDuration,
|
|
568
566
|
epochZeroMinDuration,
|
|
@@ -571,8 +569,6 @@ export const stakingActions = (
|
|
|
571
569
|
] = await Promise.all([
|
|
572
570
|
contract.read.epoch() as Promise<bigint>,
|
|
573
571
|
contract.read.finalized() as Promise<bigint>,
|
|
574
|
-
contract.read.validatorMinStake() as Promise<bigint>,
|
|
575
|
-
contract.read.delegatorMinStake() as Promise<bigint>,
|
|
576
572
|
contract.read.activeValidatorsCount() as Promise<bigint>,
|
|
577
573
|
contract.read.epochMinDuration() as Promise<bigint>,
|
|
578
574
|
contract.read.epochZeroMinDuration() as Promise<bigint>,
|
|
@@ -580,8 +576,21 @@ export const stakingActions = (
|
|
|
580
576
|
contract.read.epochEven() as Promise<any>,
|
|
581
577
|
]);
|
|
582
578
|
|
|
583
|
-
//
|
|
584
|
-
const
|
|
579
|
+
// epochOdd/epochEven return arrays: [start, end, inflation, weight, weightDeposit, weightWithdrawal, vcount, claimed, stakeDeposit, stakeWithdrawal, slashed]
|
|
580
|
+
const raw = epoch % 2n === 0n ? epochEven : epochOdd;
|
|
581
|
+
const currentEpochData = {
|
|
582
|
+
start: raw[0] as bigint,
|
|
583
|
+
end: raw[1] as bigint,
|
|
584
|
+
inflation: raw[2] as bigint,
|
|
585
|
+
weight: raw[3] as bigint,
|
|
586
|
+
weightDeposit: raw[4] as bigint,
|
|
587
|
+
weightWithdrawal: raw[5] as bigint,
|
|
588
|
+
vcount: raw[6] as bigint,
|
|
589
|
+
claimed: raw[7] as bigint,
|
|
590
|
+
stakeDeposit: raw[8] as bigint,
|
|
591
|
+
stakeWithdrawal: raw[9] as bigint,
|
|
592
|
+
slashed: raw[10] as bigint,
|
|
593
|
+
};
|
|
585
594
|
const currentEpochEnd = currentEpochData.end > 0n;
|
|
586
595
|
|
|
587
596
|
// Estimate next epoch: current start + min duration (if epoch hasn't ended)
|
|
@@ -595,10 +604,6 @@ export const stakingActions = (
|
|
|
595
604
|
return {
|
|
596
605
|
currentEpoch: epoch,
|
|
597
606
|
lastFinalizedEpoch: finalized,
|
|
598
|
-
validatorMinStake: formatStakingAmount(validatorMinStake),
|
|
599
|
-
validatorMinStakeRaw: validatorMinStake,
|
|
600
|
-
delegatorMinStake: formatStakingAmount(delegatorMinStake),
|
|
601
|
-
delegatorMinStakeRaw: delegatorMinStake,
|
|
602
607
|
activeValidatorsCount: activeCount,
|
|
603
608
|
epochMinDuration,
|
|
604
609
|
nextEpochEstimate,
|
|
@@ -624,20 +629,21 @@ export const stakingActions = (
|
|
|
624
629
|
throw new Error(`Epoch ${epochNumber} data no longer available (only current and previous epoch stored)`);
|
|
625
630
|
}
|
|
626
631
|
|
|
627
|
-
|
|
632
|
+
// epochOdd/epochEven return arrays: [start, end, inflation, weight, weightDeposit, weightWithdrawal, vcount, claimed, stakeDeposit, stakeWithdrawal, slashed]
|
|
633
|
+
const raw = epochNumber % 2n === 0n ? epochEven : epochOdd;
|
|
628
634
|
|
|
629
635
|
return {
|
|
630
|
-
start:
|
|
631
|
-
end:
|
|
632
|
-
inflation:
|
|
633
|
-
weight:
|
|
634
|
-
weightDeposit:
|
|
635
|
-
weightWithdrawal:
|
|
636
|
-
vcount:
|
|
637
|
-
claimed:
|
|
638
|
-
stakeDeposit:
|
|
639
|
-
stakeWithdrawal:
|
|
640
|
-
slashed:
|
|
636
|
+
start: raw[0] as bigint,
|
|
637
|
+
end: raw[1] as bigint,
|
|
638
|
+
inflation: raw[2] as bigint,
|
|
639
|
+
weight: raw[3] as bigint,
|
|
640
|
+
weightDeposit: raw[4] as bigint,
|
|
641
|
+
weightWithdrawal: raw[5] as bigint,
|
|
642
|
+
vcount: raw[6] as bigint,
|
|
643
|
+
claimed: raw[7] as bigint,
|
|
644
|
+
stakeDeposit: raw[8] as bigint,
|
|
645
|
+
stakeWithdrawal: raw[9] as bigint,
|
|
646
|
+
slashed: raw[10] as bigint,
|
|
641
647
|
};
|
|
642
648
|
},
|
|
643
649
|
|
|
@@ -654,7 +660,7 @@ export const stakingActions = (
|
|
|
654
660
|
|
|
655
661
|
getQuarantinedValidators: async (): Promise<Address[]> => {
|
|
656
662
|
const contract = getReadOnlyStakingContract();
|
|
657
|
-
return contract.read.
|
|
663
|
+
return contract.read.getValidatorQuarantineList() as Promise<Address[]>;
|
|
658
664
|
},
|
|
659
665
|
|
|
660
666
|
getBannedValidators: async (startIndex = 0n, size = 100n): Promise<BannedValidatorInfo[]> => {
|
|
@@ -677,13 +683,6 @@ export const stakingActions = (
|
|
|
677
683
|
}));
|
|
678
684
|
},
|
|
679
685
|
|
|
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
686
|
getStakingContract,
|
|
688
687
|
parseStakingAmount,
|
|
689
688
|
formatStakingAmount,
|
package/src/types/staking.ts
CHANGED
|
@@ -120,10 +120,6 @@ export interface EpochData {
|
|
|
120
120
|
export interface EpochInfo {
|
|
121
121
|
currentEpoch: bigint;
|
|
122
122
|
lastFinalizedEpoch: bigint;
|
|
123
|
-
validatorMinStake: string;
|
|
124
|
-
validatorMinStakeRaw: bigint;
|
|
125
|
-
delegatorMinStake: string;
|
|
126
|
-
delegatorMinStakeRaw: bigint;
|
|
127
123
|
activeValidatorsCount: bigint;
|
|
128
124
|
epochMinDuration: bigint;
|
|
129
125
|
nextEpochEstimate: Date | null;
|
|
@@ -218,7 +214,6 @@ export interface StakingActions {
|
|
|
218
214
|
getEpochData: (epochNumber: bigint) => Promise<EpochData>;
|
|
219
215
|
getActiveValidators: () => Promise<Address[]>;
|
|
220
216
|
getActiveValidatorsCount: () => Promise<bigint>;
|
|
221
|
-
getSlashingAddress: () => Promise<Address>;
|
|
222
217
|
getStakingContract: () => StakingContract;
|
|
223
218
|
parseStakingAmount: (amount: string | bigint) => bigint;
|
|
224
219
|
formatStakingAmount: (amount: bigint) => string;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
// tests/smoke.test.ts
|
|
2
|
+
// Smoke tests against live testnet-asimov to verify ABI compatibility and connectivity.
|
|
3
|
+
// Run with: npm run test:smoke
|
|
4
|
+
// These are excluded from regular `npm test` to avoid CI dependence on testnet availability.
|
|
5
|
+
|
|
6
|
+
import {describe, it, expect, beforeAll} from "vitest";
|
|
7
|
+
import {createPublicClient, http, webSocket, getContract, Address as ViemAddress} from "viem";
|
|
8
|
+
import {testnetAsimov} from "@/chains/testnetAsimov";
|
|
9
|
+
import {createClient} from "@/client/client";
|
|
10
|
+
import {STAKING_ABI} from "@/abi/staking";
|
|
11
|
+
import {Address} from "@/types/accounts";
|
|
12
|
+
|
|
13
|
+
const TIMEOUT = 30_000;
|
|
14
|
+
|
|
15
|
+
// ─── HTTP RPC Connectivity ───────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
describe("Testnet Asimov - HTTP RPC", () => {
|
|
18
|
+
it("should fetch chain ID", async () => {
|
|
19
|
+
const client = createPublicClient({
|
|
20
|
+
chain: testnetAsimov,
|
|
21
|
+
transport: http(testnetAsimov.rpcUrls.default.http[0]),
|
|
22
|
+
});
|
|
23
|
+
const chainId = await client.getChainId();
|
|
24
|
+
expect(chainId).toBe(testnetAsimov.id);
|
|
25
|
+
}, TIMEOUT);
|
|
26
|
+
|
|
27
|
+
it("should fetch latest block number", async () => {
|
|
28
|
+
const client = createPublicClient({
|
|
29
|
+
chain: testnetAsimov,
|
|
30
|
+
transport: http(testnetAsimov.rpcUrls.default.http[0]),
|
|
31
|
+
});
|
|
32
|
+
const blockNumber = await client.getBlockNumber();
|
|
33
|
+
expect(blockNumber).toBeGreaterThan(0n);
|
|
34
|
+
}, TIMEOUT);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// ─── WebSocket RPC Connectivity ──────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
describe("Testnet Asimov - WebSocket RPC", () => {
|
|
40
|
+
const wsUrl = testnetAsimov.rpcUrls.default.webSocket?.[0];
|
|
41
|
+
|
|
42
|
+
it("should have a WS URL configured", () => {
|
|
43
|
+
expect(wsUrl).toBeDefined();
|
|
44
|
+
expect(wsUrl).toMatch(/^wss?:\/\//);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should connect and fetch chain ID over WebSocket", async () => {
|
|
48
|
+
if (!wsUrl) return;
|
|
49
|
+
const client = createPublicClient({
|
|
50
|
+
chain: testnetAsimov,
|
|
51
|
+
transport: webSocket(wsUrl),
|
|
52
|
+
});
|
|
53
|
+
const chainId = await client.getChainId();
|
|
54
|
+
// WS endpoint may point to the underlying chain (different ID from GenLayer overlay)
|
|
55
|
+
// The key assertion is that the connection works and returns a valid number
|
|
56
|
+
expect(chainId).toBeTypeOf("number");
|
|
57
|
+
expect(chainId).toBeGreaterThan(0);
|
|
58
|
+
if (chainId !== testnetAsimov.id) {
|
|
59
|
+
console.warn(
|
|
60
|
+
`WS chain ID (${chainId}) differs from HTTP chain ID (${testnetAsimov.id}). ` +
|
|
61
|
+
`WS URL may point to the underlying L1/L2 chain.`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}, TIMEOUT);
|
|
65
|
+
|
|
66
|
+
it("should fetch latest block number over WebSocket", async () => {
|
|
67
|
+
if (!wsUrl) return;
|
|
68
|
+
const client = createPublicClient({
|
|
69
|
+
chain: testnetAsimov,
|
|
70
|
+
transport: webSocket(wsUrl),
|
|
71
|
+
});
|
|
72
|
+
const blockNumber = await client.getBlockNumber();
|
|
73
|
+
expect(blockNumber).toBeGreaterThan(0n);
|
|
74
|
+
}, TIMEOUT);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// ─── Staking Read-Only via WebSocket ─────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
describe("Testnet Asimov - Staking over WebSocket", () => {
|
|
80
|
+
const wsUrl = testnetAsimov.rpcUrls.default.webSocket?.[0];
|
|
81
|
+
const stakingAddress = testnetAsimov.stakingContract?.address as ViemAddress;
|
|
82
|
+
|
|
83
|
+
// First check if WS points to the same chain — if not, skip staking tests
|
|
84
|
+
let wsMatchesChain = false;
|
|
85
|
+
let wsPub: ReturnType<typeof createPublicClient> | null = null;
|
|
86
|
+
|
|
87
|
+
beforeAll(async () => {
|
|
88
|
+
if (!wsUrl) return;
|
|
89
|
+
wsPub = createPublicClient({chain: testnetAsimov, transport: webSocket(wsUrl)});
|
|
90
|
+
try {
|
|
91
|
+
const chainId = await wsPub.getChainId();
|
|
92
|
+
wsMatchesChain = chainId === testnetAsimov.id;
|
|
93
|
+
if (!wsMatchesChain) {
|
|
94
|
+
console.warn(
|
|
95
|
+
`WS chain ID (${chainId}) differs from testnet (${testnetAsimov.id}). ` +
|
|
96
|
+
`Staking contract calls will be skipped — WS endpoint serves a different chain.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
console.warn("WS connection failed during setup");
|
|
101
|
+
}
|
|
102
|
+
}, TIMEOUT);
|
|
103
|
+
|
|
104
|
+
it("epoch() via WS", async () => {
|
|
105
|
+
if (!wsMatchesChain || !wsPub) return;
|
|
106
|
+
const contract = getContract({address: stakingAddress, abi: STAKING_ABI, client: wsPub});
|
|
107
|
+
const epoch = await contract.read.epoch();
|
|
108
|
+
expect(epoch).toBeTypeOf("bigint");
|
|
109
|
+
}, TIMEOUT);
|
|
110
|
+
|
|
111
|
+
it("activeValidatorsCount() via WS", async () => {
|
|
112
|
+
if (!wsMatchesChain || !wsPub) return;
|
|
113
|
+
const contract = getContract({address: stakingAddress, abi: STAKING_ABI, client: wsPub});
|
|
114
|
+
const count = await contract.read.activeValidatorsCount();
|
|
115
|
+
expect(count).toBeTypeOf("bigint");
|
|
116
|
+
expect(count).toBeGreaterThanOrEqual(0n);
|
|
117
|
+
}, TIMEOUT);
|
|
118
|
+
|
|
119
|
+
it("activeValidators() via WS", async () => {
|
|
120
|
+
if (!wsMatchesChain || !wsPub) return;
|
|
121
|
+
const contract = getContract({address: stakingAddress, abi: STAKING_ABI, client: wsPub});
|
|
122
|
+
const validators = await contract.read.activeValidators();
|
|
123
|
+
expect(Array.isArray(validators)).toBe(true);
|
|
124
|
+
}, TIMEOUT);
|
|
125
|
+
|
|
126
|
+
it("isValidator() via WS", async () => {
|
|
127
|
+
if (!wsMatchesChain || !wsPub) return;
|
|
128
|
+
const contract = getContract({address: stakingAddress, abi: STAKING_ABI, client: wsPub});
|
|
129
|
+
const validators = (await contract.read.activeValidators()) as ViemAddress[];
|
|
130
|
+
const nonZero = validators.filter(v => v !== "0x0000000000000000000000000000000000000000");
|
|
131
|
+
if (nonZero.length === 0) return;
|
|
132
|
+
|
|
133
|
+
const result = await contract.read.isValidator([nonZero[0]]);
|
|
134
|
+
expect(result).toBe(true);
|
|
135
|
+
}, TIMEOUT);
|
|
136
|
+
|
|
137
|
+
it("validatorView() via WS", async () => {
|
|
138
|
+
if (!wsMatchesChain || !wsPub) return;
|
|
139
|
+
const contract = getContract({address: stakingAddress, abi: STAKING_ABI, client: wsPub});
|
|
140
|
+
const validators = (await contract.read.activeValidators()) as ViemAddress[];
|
|
141
|
+
const nonZero = validators.filter(v => v !== "0x0000000000000000000000000000000000000000");
|
|
142
|
+
if (nonZero.length === 0) return;
|
|
143
|
+
|
|
144
|
+
const view = await contract.read.validatorView([nonZero[0]]) as unknown as readonly unknown[];
|
|
145
|
+
expect(Array.isArray(view)).toBe(true);
|
|
146
|
+
// view is [left, right, parent, eBanned, ePrimed, vStake, vShares, dStake, dShares, vDeposit, vWithdrawal, live]
|
|
147
|
+
expect(view.length).toBe(12);
|
|
148
|
+
}, TIMEOUT);
|
|
149
|
+
|
|
150
|
+
it("getValidatorQuarantineList() via WS", async () => {
|
|
151
|
+
if (!wsMatchesChain || !wsPub) return;
|
|
152
|
+
const contract = getContract({address: stakingAddress, abi: STAKING_ABI, client: wsPub});
|
|
153
|
+
const list = await contract.read.getValidatorQuarantineList();
|
|
154
|
+
expect(Array.isArray(list)).toBe(true);
|
|
155
|
+
}, TIMEOUT);
|
|
156
|
+
|
|
157
|
+
it("epochOdd() / epochEven() via WS", async () => {
|
|
158
|
+
if (!wsMatchesChain || !wsPub) return;
|
|
159
|
+
const contract = getContract({address: stakingAddress, abi: STAKING_ABI, client: wsPub});
|
|
160
|
+
const odd = await contract.read.epochOdd();
|
|
161
|
+
const even = await contract.read.epochEven();
|
|
162
|
+
expect(Array.isArray(odd)).toBe(true);
|
|
163
|
+
expect(Array.isArray(even)).toBe(true);
|
|
164
|
+
expect(odd.length).toBe(11);
|
|
165
|
+
expect(even.length).toBe(11);
|
|
166
|
+
}, TIMEOUT);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// ─── Staking Read-Only Methods ───────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
describe("Testnet Asimov - Staking (read-only)", () => {
|
|
172
|
+
let client: ReturnType<typeof createClient>;
|
|
173
|
+
|
|
174
|
+
beforeAll(() => {
|
|
175
|
+
client = createClient({chain: testnetAsimov});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("getEpochInfo", async () => {
|
|
179
|
+
const info = await client.getEpochInfo();
|
|
180
|
+
expect(info.currentEpoch).toBeTypeOf("bigint");
|
|
181
|
+
expect(info.lastFinalizedEpoch).toBeTypeOf("bigint");
|
|
182
|
+
expect(info.activeValidatorsCount).toBeTypeOf("bigint");
|
|
183
|
+
expect(info.epochMinDuration).toBeTypeOf("bigint");
|
|
184
|
+
// nextEpochEstimate is Date | null
|
|
185
|
+
if (info.nextEpochEstimate !== null) {
|
|
186
|
+
expect(info.nextEpochEstimate).toBeInstanceOf(Date);
|
|
187
|
+
}
|
|
188
|
+
}, TIMEOUT);
|
|
189
|
+
|
|
190
|
+
it("getActiveValidatorsCount", async () => {
|
|
191
|
+
const count = await client.getActiveValidatorsCount();
|
|
192
|
+
expect(count).toBeTypeOf("bigint");
|
|
193
|
+
expect(count).toBeGreaterThanOrEqual(0n);
|
|
194
|
+
}, TIMEOUT);
|
|
195
|
+
|
|
196
|
+
it("getActiveValidators", async () => {
|
|
197
|
+
const validators = await client.getActiveValidators();
|
|
198
|
+
expect(Array.isArray(validators)).toBe(true);
|
|
199
|
+
// Each entry should be a hex address
|
|
200
|
+
for (const v of validators) {
|
|
201
|
+
expect(v).toMatch(/^0x[0-9a-fA-F]{40}$/);
|
|
202
|
+
}
|
|
203
|
+
}, TIMEOUT);
|
|
204
|
+
|
|
205
|
+
it("getEpochData for current epoch", async () => {
|
|
206
|
+
const {currentEpoch} = await client.getEpochInfo();
|
|
207
|
+
const data = await client.getEpochData(currentEpoch);
|
|
208
|
+
expect(data.start).toBeTypeOf("bigint");
|
|
209
|
+
expect(data.weight).toBeTypeOf("bigint");
|
|
210
|
+
expect(data.vcount).toBeTypeOf("bigint");
|
|
211
|
+
}, TIMEOUT);
|
|
212
|
+
|
|
213
|
+
it("isValidator returns boolean", async () => {
|
|
214
|
+
const validators = await client.getActiveValidators();
|
|
215
|
+
if (validators.length === 0) return; // nothing to test
|
|
216
|
+
|
|
217
|
+
const result = await client.isValidator(validators[0]);
|
|
218
|
+
expect(result).toBe(true);
|
|
219
|
+
|
|
220
|
+
// zero address should not be a validator
|
|
221
|
+
const fake = await client.isValidator("0x0000000000000000000000000000000000000001" as Address);
|
|
222
|
+
expect(fake).toBe(false);
|
|
223
|
+
}, TIMEOUT);
|
|
224
|
+
|
|
225
|
+
it("getValidatorInfo for an active validator", async () => {
|
|
226
|
+
const validators = await client.getActiveValidators();
|
|
227
|
+
if (validators.length === 0) return;
|
|
228
|
+
|
|
229
|
+
const info = await client.getValidatorInfo(validators[0]);
|
|
230
|
+
expect(info.address).toBe(validators[0]);
|
|
231
|
+
expect(info.owner).toMatch(/^0x[0-9a-fA-F]{40}$/);
|
|
232
|
+
expect(info.operator).toMatch(/^0x[0-9a-fA-F]{40}$/);
|
|
233
|
+
expect(info.vStakeRaw).toBeTypeOf("bigint");
|
|
234
|
+
expect(typeof info.live).toBe("boolean");
|
|
235
|
+
expect(typeof info.banned).toBe("boolean");
|
|
236
|
+
expect(typeof info.needsPriming).toBe("boolean");
|
|
237
|
+
expect(Array.isArray(info.pendingDeposits)).toBe(true);
|
|
238
|
+
expect(Array.isArray(info.pendingWithdrawals)).toBe(true);
|
|
239
|
+
}, TIMEOUT);
|
|
240
|
+
|
|
241
|
+
it("getStakeInfo for validator self-stake", async () => {
|
|
242
|
+
const validators = await client.getActiveValidators();
|
|
243
|
+
if (validators.length === 0) return;
|
|
244
|
+
|
|
245
|
+
const validatorAddr = validators[0];
|
|
246
|
+
// Self-stake: delegator = validator address
|
|
247
|
+
const stakeInfo = await client.getStakeInfo(validatorAddr, validatorAddr);
|
|
248
|
+
expect(stakeInfo.delegator).toBe(validatorAddr);
|
|
249
|
+
expect(stakeInfo.validator).toBe(validatorAddr);
|
|
250
|
+
expect(stakeInfo.shares).toBeTypeOf("bigint");
|
|
251
|
+
expect(stakeInfo.stakeRaw).toBeTypeOf("bigint");
|
|
252
|
+
expect(Array.isArray(stakeInfo.pendingDeposits)).toBe(true);
|
|
253
|
+
expect(Array.isArray(stakeInfo.pendingWithdrawals)).toBe(true);
|
|
254
|
+
}, TIMEOUT);
|
|
255
|
+
|
|
256
|
+
it("getQuarantinedValidators returns array", async () => {
|
|
257
|
+
// This calls getValidatorQuarantineList() — the v0.5 renamed function
|
|
258
|
+
const quarantined = await (client as any).getQuarantinedValidators();
|
|
259
|
+
expect(Array.isArray(quarantined)).toBe(true);
|
|
260
|
+
}, TIMEOUT);
|
|
261
|
+
|
|
262
|
+
it("getBannedValidators returns array", async () => {
|
|
263
|
+
const banned = await (client as any).getBannedValidators();
|
|
264
|
+
expect(Array.isArray(banned)).toBe(true);
|
|
265
|
+
for (const b of banned) {
|
|
266
|
+
expect(b.validator).toMatch(/^0x[0-9a-fA-F]{40}$/);
|
|
267
|
+
expect(b.untilEpoch).toBeTypeOf("bigint");
|
|
268
|
+
expect(typeof b.permanentlyBanned).toBe("boolean");
|
|
269
|
+
}
|
|
270
|
+
}, TIMEOUT);
|
|
271
|
+
|
|
272
|
+
it("getStakingContract returns a contract instance", () => {
|
|
273
|
+
const contract = client.getStakingContract();
|
|
274
|
+
expect(contract).toBeDefined();
|
|
275
|
+
expect(contract.address).toMatch(/^0x[0-9a-fA-F]{40}$/);
|
|
276
|
+
expect(contract.read).toBeDefined();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("parseStakingAmount and formatStakingAmount round-trip", () => {
|
|
280
|
+
// parseStakingAmount treats bare strings as wei; use "gen" suffix for human amounts
|
|
281
|
+
const amount = client.parseStakingAmount("1.5gen");
|
|
282
|
+
expect(amount).toBeTypeOf("bigint");
|
|
283
|
+
expect(amount).toBe(1500000000000000000n);
|
|
284
|
+
const formatted = client.formatStakingAmount(amount);
|
|
285
|
+
expect(formatted).toBe("1.5 GEN");
|
|
286
|
+
|
|
287
|
+
// Raw wei round-trip
|
|
288
|
+
const weiAmount = client.parseStakingAmount("42000000000000000000");
|
|
289
|
+
expect(client.formatStakingAmount(weiAmount)).toBe("42 GEN");
|
|
290
|
+
});
|
|
291
|
+
});
|
package/vitest.config.ts
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {defineConfig} from "vitest/config";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
test: {
|
|
6
|
+
globals: true,
|
|
7
|
+
environment: "node",
|
|
8
|
+
include: ["tests/smoke.test.ts"],
|
|
9
|
+
testTimeout: 30_000,
|
|
10
|
+
},
|
|
11
|
+
resolve: {
|
|
12
|
+
alias: {
|
|
13
|
+
"@": path.resolve(__dirname, "./src"),
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|