datahaven/test/moonwall/suites/dev/common/test-precompile/test-precompile-batch.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

272 lines
8.2 KiB
TypeScript

import { describeSuite, expect, fetchCompiledContract } from "@moonwall/cli";
import {
ALITH_ADDRESS,
ALITH_PRIVATE_KEY,
BALTATHAR_ADDRESS,
BALTATHAR_PRIVATE_KEY,
CHARLETH_ADDRESS,
createViemTransaction,
sendRawTransaction
} from "@moonwall/util";
import { encodeFunctionData, fromHex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { ConstantStore, expectEVMResult, getSignatureParameters } from "../../../../helpers";
const ALITH_ACCOUNT = privateKeyToAccount(ALITH_PRIVATE_KEY);
// Precompile addresses for DataHaven
const PRECOMPILE_BATCH_ADDRESS = "0x0000000000000000000000000000000000000808";
const PRECOMPILE_CALL_PERMIT_ADDRESS = "0x000000000000000000000000000000000000080a";
describeSuite({
id: "D010312",
title: "Batch Precompile",
foundationMethods: "dev",
testCases: ({ context, it }) => {
it({
id: "T01",
title: "all batch functions should consume similar gas",
test: async () => {
const { abi: batchInterface } = fetchCompiledContract("Batch");
// each tx have a different gas limit to ensure it doesn't impact gas used
const batchAllTx = await createViemTransaction(context, {
to: PRECOMPILE_BATCH_ADDRESS,
gas: 1114112n,
data: encodeFunctionData({
abi: batchInterface,
functionName: "batchAll",
args: [
[BALTATHAR_ADDRESS, CHARLETH_ADDRESS],
["1000000000000000000", "2000000000000000000"],
[],
[]
]
})
});
const batchSomeTx = await createViemTransaction(context, {
to: PRECOMPILE_BATCH_ADDRESS,
gas: 1179648n,
nonce: 1,
data: encodeFunctionData({
abi: batchInterface,
functionName: "batchSome",
args: [
[BALTATHAR_ADDRESS, CHARLETH_ADDRESS],
["1000000000000000000", "2000000000000000000"],
[],
[]
]
})
});
const batchSomeUntilFailureTx = await createViemTransaction(context, {
to: PRECOMPILE_BATCH_ADDRESS,
gas: 1245184n,
nonce: 2,
data: encodeFunctionData({
abi: batchInterface,
functionName: "batchSomeUntilFailure",
args: [
[BALTATHAR_ADDRESS, CHARLETH_ADDRESS],
["1000000000000000000", "2000000000000000000"],
[],
[]
]
})
});
const batchAllResult = await sendRawTransaction(context, batchAllTx);
const batchSomeResult = await sendRawTransaction(context, batchSomeTx);
const batchSomeUntilFailureResult = await sendRawTransaction(
context,
batchSomeUntilFailureTx
);
await context.createBlock();
const batchAllReceipt = await context
.viem()
.getTransactionReceipt({ hash: batchAllResult as `0x${string}` });
const batchSomeReceipt = await context
.viem()
.getTransactionReceipt({ hash: batchSomeResult as `0x${string}` });
const batchSomeUntilFailureReceipt = await context
.viem()
.getTransactionReceipt({ hash: batchSomeUntilFailureResult as `0x${string}` });
const STORAGE_READ_GAS_COST = // One storage read gas cost
ConstantStore(context).STORAGE_READ_COST / ConstantStore(context).WEIGHT_TO_GAS_RATIO;
// All batch functions should use similar gas (within reasonable tolerance)
// The actual gas used includes one storage read for balance check
const expectedGas = 43932n + STORAGE_READ_GAS_COST;
expect(batchAllReceipt.gasUsed).toBe(expectedGas);
expect(batchSomeReceipt.gasUsed).toBe(expectedGas);
expect(batchSomeUntilFailureReceipt.gasUsed).toBe(expectedGas);
}
});
it({
id: "T02",
title: "batch should be able to call itself",
test: async () => {
const { abi: batchInterface } = fetchCompiledContract("Batch");
const batchAll = await context.writeContract!({
contractAddress: PRECOMPILE_BATCH_ADDRESS,
contractName: "Batch",
functionName: "batchAll",
args: [
[PRECOMPILE_BATCH_ADDRESS],
[],
[
encodeFunctionData({
abi: batchInterface,
functionName: "batchAll",
args: [
[BALTATHAR_ADDRESS, CHARLETH_ADDRESS],
["1000000000000000000", "2000000000000000000"],
[],
[]
]
})
],
[]
],
rawTxOnly: true
});
const { result } = await context.createBlock(batchAll);
expectEVMResult(result!.events, "Succeed");
}
});
it({
id: "T03",
title: "batch should be callable from call permit precompile",
test: async () => {
const { abi: batchInterface } = fetchCompiledContract("Batch");
const { abi: callPermitAbi } = fetchCompiledContract("CallPermit");
const alithNonceResult = (
await context.viem().call({
to: PRECOMPILE_CALL_PERMIT_ADDRESS,
data: encodeFunctionData({
abi: callPermitAbi,
functionName: "nonces",
args: [ALITH_ADDRESS]
})
})
).data;
const batchData = encodeFunctionData({
abi: batchInterface,
functionName: "batchAll",
args: [
[BALTATHAR_ADDRESS, CHARLETH_ADDRESS],
["1000000000000000000", "2000000000000000000"],
[],
[]
]
});
const chainId = await context.viem().getChainId();
const signature = await ALITH_ACCOUNT.signTypedData({
types: {
EIP712Domain: [
{
name: "name",
type: "string"
},
{
name: "version",
type: "string"
},
{
name: "chainId",
type: "uint256"
},
{
name: "verifyingContract",
type: "address"
}
],
CallPermit: [
{
name: "from",
type: "address"
},
{
name: "to",
type: "address"
},
{
name: "value",
type: "uint256"
},
{
name: "data",
type: "bytes"
},
{
name: "gaslimit",
type: "uint64"
},
{
name: "nonce",
type: "uint256"
},
{
name: "deadline",
type: "uint256"
}
]
},
primaryType: "CallPermit",
domain: {
name: "Call Permit Precompile",
version: "1",
chainId: Number(chainId),
verifyingContract: PRECOMPILE_CALL_PERMIT_ADDRESS
},
message: {
from: ALITH_ADDRESS,
to: PRECOMPILE_BATCH_ADDRESS,
value: 0n,
data: batchData,
gaslimit: 200_000n,
nonce: fromHex(alithNonceResult!, "bigint"),
deadline: 9999999999n
}
});
const { v, r, s } = getSignatureParameters(signature);
const { result: baltatharForAlithResult } = await context.createBlock(
await createViemTransaction(context, {
privateKey: BALTATHAR_PRIVATE_KEY,
to: PRECOMPILE_CALL_PERMIT_ADDRESS,
data: encodeFunctionData({
abi: callPermitAbi,
functionName: "dispatch",
args: [
ALITH_ADDRESS,
PRECOMPILE_BATCH_ADDRESS,
0,
batchData,
200_000,
9999999999,
v,
r,
s
]
})
})
);
expectEVMResult(baltatharForAlithResult!.events, "Succeed");
}
});
}
});