datahaven/test/e2e/suites/rewards-message.test.ts
Steve Degosserie 17c215d047
refactor(test): reorganize e2e test suites (#373)
## Summary

Reorganizes the test directory structure for better clarity and
maintainability:

- **Rename `test/datahaven/` → `test/moonwall/`**: Clearly identifies
these as Moonwall single-node tests
- **Move `test/framework/` → `test/e2e/framework/`**: Groups e2e test
utilities under a dedicated folder
- **Move `test/suites/` → `test/e2e/suites/`**: Groups e2e test suites
with the framework
- **Add `test/e2e/framework/validators.ts`**: Extracts validator test
helpers from utils into the e2e framework
- **Update documentation**: README.md and E2E_FRAMEWORK_OVERVIEW.md
reflect the new structure

### New Directory Structure

```
test/
├── e2e/
│   ├── suites/          # E2E test suites (Kurtosis-based)
│   └── framework/       # E2E test utilities & helpers
├── moonwall/            # Moonwall single-node tests
├── launcher/            # Network deployment tools
└── ...
```

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 15:52:33 +02:00

171 lines
5.8 KiB
TypeScript

import { beforeAll, describe, expect, it } from "bun:test";
import { FixedSizeBinary } from "polkadot-api";
import { CROSS_CHAIN_TIMEOUTS, getEvmEcdsaSigner, logger, SUBSTRATE_FUNDED_ACCOUNTS } from "utils";
import type { Address } from "viem";
import { getContractInstance, parseDeploymentsFile } from "../../utils/contracts";
import { waitForDataHavenEvent } from "../../utils/events";
import { BaseTestSuite } from "../framework";
/**
* Temporary helper to set V2 rewards parameters via sudo.
* This is needed until the launcher properly configures these parameters.
*/
async function setV2RewardsParameters(dhApi: any) {
const signer = getEvmEcdsaSigner(SUBSTRATE_FUNDED_ACCOUNTS.ALITH.privateKey);
// Get addresses from deployments
const deployments = await parseDeploymentsFile();
const whaveTokenAddress =
deployments.DeployedStrategies?.[0]?.underlyingToken ??
"0x95401dc811bb5740090279Ba06cfA8fcF6113778";
const strategyAddress =
deployments.DeployedStrategies?.[0]?.address ?? "0x0000000000000000000000000000000000000000";
const serviceManagerAddress = deployments.ServiceManager;
// Set RewardsGenesisTimestamp to 1 day ago (aligned to day boundary) to ensure valid rewards periods
const genesisTimestamp = Math.floor((Date.now() / 1000 - 86400) / 86400) * 86400;
logger.debug(
"Setting V2 rewards parameters:\n" +
` WHAVETokenAddress=${whaveTokenAddress}\n` +
` StrategyAddress=${strategyAddress}\n` +
` ServiceManagerAddress=${serviceManagerAddress}\n` +
` RewardsGenesisTimestamp=${genesisTimestamp}`
);
// Build sudo calls to set parameters
const calls = [
// Set ServiceManager address (required for rewards submission)
dhApi.tx.Parameters.set_parameter({
key_value: {
type: "RuntimeConfig",
value: {
type: "DatahavenServiceManagerAddress",
value: [new FixedSizeBinary(Buffer.from(serviceManagerAddress.slice(2), "hex"))]
}
}
}).decodedCall,
dhApi.tx.Parameters.set_parameter({
key_value: {
type: "RuntimeConfig",
value: {
type: "WHAVETokenAddress",
value: [new FixedSizeBinary(Buffer.from(whaveTokenAddress.slice(2), "hex"))]
}
}
}).decodedCall,
dhApi.tx.Parameters.set_parameter({
key_value: {
type: "RuntimeConfig",
value: {
type: "RewardsGenesisTimestamp",
value: [genesisTimestamp]
}
}
}).decodedCall,
// Set strategies and multipliers: [(strategy_address, multiplier)]
dhApi.tx.Parameters.set_parameter({
key_value: {
type: "RuntimeConfig",
value: {
type: "RewardsStrategiesAndMultipliers",
value: [[[new FixedSizeBinary(Buffer.from(strategyAddress.slice(2), "hex")), 1n]]]
}
}
}).decodedCall
];
const tx = dhApi.tx.Sudo.sudo({
call: dhApi.tx.Utility.batch_all({ calls }).decodedCall
});
const result = await tx.signAndSubmit(signer);
if (!result.ok) {
throw new Error("Failed to set V2 rewards parameters");
}
logger.debug("V2 rewards parameters set successfully");
}
class RewardsMessageTestSuite extends BaseTestSuite {
constructor() {
super({
suiteName: "rewards-message"
});
this.setupHooks();
}
}
const suite = new RewardsMessageTestSuite();
describe("Rewards Message Flow", () => {
let serviceManager!: any;
let publicClient!: any;
let dhApi!: any;
let eraIndex!: number;
let totalPoints!: bigint;
beforeAll(async () => {
const connectors = suite.getTestConnectors();
publicClient = connectors.publicClient;
dhApi = connectors.dhApi;
serviceManager = await getContractInstance("ServiceManager");
// Set V2 rewards parameters (temporary until launcher configures them)
await setV2RewardsParameters(dhApi);
});
it("should verify rewards infrastructure deployment", async () => {
const gateway = await getContractInstance("Gateway");
expect(serviceManager.address).toBeDefined();
expect(gateway.address).toBeDefined();
const rewardsInitiator = (await publicClient.readContract({
address: serviceManager.address,
abi: serviceManager.abi,
functionName: "rewardsInitiator",
args: []
})) as Address;
// ServiceManager must have a rewardsInitiator configured for EigenLayer rewards submission
expect(rewardsInitiator).toBeDefined();
logger.debug(`ServiceManager rewardsInitiator: ${rewardsInitiator}`);
});
it("should wait for era end and emit RewardsMessageSent", async () => {
// Get current era for event filtering
const currentEra = await dhApi.query.ExternalValidators.ActiveEra.getValue();
const currentEraIndex = currentEra?.index ?? 0;
logger.debug(`Waiting for RewardsMessageSent for era ${currentEraIndex}`);
const payload = await waitForDataHavenEvent<any>({
api: dhApi,
pallet: "ExternalValidatorsRewards",
event: "RewardsMessageSent",
filter: (e) => e.era_index === currentEraIndex,
timeout: CROSS_CHAIN_TIMEOUTS.DH_TO_ETH_MS
});
expect(payload).toBeDefined();
totalPoints = payload.total_points;
eraIndex = payload.era_index;
expect(totalPoints).toBeGreaterThan(0n);
logger.debug(`RewardsMessageSent received: era=${eraIndex}, totalPoints=${totalPoints}`);
});
it("should verify reward points were recorded for the era", async () => {
// Verify reward points were recorded on DataHaven side
const rewardPoints =
await dhApi.query.ExternalValidatorsRewards.RewardPointsForEra.getValue(eraIndex);
expect(rewardPoints).toBeDefined();
expect(rewardPoints.total).toBeGreaterThan(0);
const validatorsWithPoints = rewardPoints.individual.length;
logger.debug(
`Era ${eraIndex}: ${validatorsWithPoints} validators earned ${rewardPoints.total} total points`
);
});
});