Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/typescript-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ jobs:
binary: fast
- test: zombienet_subnets
binary: fast
- test: zombienet_evm
binary: fast

name: "typescript-e2e-${{ matrix.test }}"

Expand Down
32 changes: 32 additions & 0 deletions ts-tests/moonwall.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,38 @@
"endpoints": ["ws://127.0.0.1:9947"]
}
]
}, {
"name": "zombienet_evm",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] Wire the new EVM suite into CI

This registers a new zombienet_evm Moonwall suite, but .github/workflows/typescript-e2e.yml still only runs dev, zombienet_shield, zombienet_staking, zombienet_coldkey_swap, and zombienet_subnets. As written, the migrated EVM test can go stale because PR CI never invokes pnpm moonwall test zombienet_evm.

Add zombienet_evm to the run-e2e-tests matrix, likely against the release binary unless there is a specific fast-runtime reason.

"timeout": 600000,
"testFileDir": ["suites/zombienet_evm"],
"runScripts": [
"generate-types.sh",
"build-spec.sh"
],
"foundation": {
"type": "zombie",
"zombieSpec": {
"configPath": "./configs/zombie_node.json",
"skipBlockCheck": []
}
},
"vitestArgs": {
"bail": 1
},
"connections": [
{
"name": "Node",
"type": "papi",
"endpoints": ["ws://127.0.0.1:9947"],
"descriptor": "subtensor"
},
{
"name": "EVM",
"type": "ethers",
"endpoints": ["http://127.0.0.1:9947"],
"descriptor": "evm"
}
]
}, {
"name": "zombienet_subnets",
"timeout": 600000,
Expand Down
13 changes: 13 additions & 0 deletions ts-tests/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,16 @@ onlyBuiltDependencies:
- protobufjs
- sqlite3
- ssh2

# Allow exotic subdependencies (needed for toml dependency)
allowExoticSubdeps: true

allowBuilds:
'@biomejs/biome': set this to true or false
'@parcel/watcher': set this to true or false
cpu-features: set this to true or false
esbuild: set this to true or false
msgpackr-extract: set this to true or false
protobufjs: set this to true or false
sqlite3: set this to true or false
ssh2: set this to true or false
Comment on lines +20 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] Resolve allowBuilds placeholders

The new allowBuilds map still contains generated placeholder strings instead of explicit boolean decisions. pnpm 10.26+ defines this field as a map that explicitly allows (true) or disallows (false) dependency build scripts, so leaving set this to true or false here keeps the new policy unresolved and can cause install-time warnings or stricter future failures. (newreleases.io)

Suggested change
allowBuilds:
'@biomejs/biome': set this to true or false
'@parcel/watcher': set this to true or false
cpu-features: set this to true or false
esbuild: set this to true or false
msgpackr-extract: set this to true or false
protobufjs: set this to true or false
sqlite3: set this to true or false
ssh2: set this to true or false
allowBuilds:
'@biomejs/biome': true
'@parcel/watcher': true
cpu-features: true
esbuild: true
msgpackr-extract: true
protobufjs: true
sqlite3: true
ssh2: true

Comment on lines +20 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] Resolve allowBuilds placeholders

This allowBuilds block still contains generated placeholder values instead of concrete booleans. Since the same packages are already listed under onlyBuiltDependencies, leaving these placeholder strings makes the workspace policy ambiguous and can confuse future pnpm maintenance. Either remove the unfinished block or replace each entry with intentional true/false values.

Comment on lines +20 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] Resolve allowBuilds placeholders

The allowBuilds entries are still literal placeholder strings (set this to true or false). Resolve this before merge by either replacing them with explicit boolean policy values or removing the block if onlyBuiltDependencies above is the intended source of truth. Leaving generated placeholders makes the install policy ambiguous and can confuse future pnpm approve-builds or dependency review work.

Comment on lines +21 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] Resolve allowBuilds placeholders

These entries are still the literal placeholder strings from pnpm approve-builds. Please replace each value with an explicit boolean policy, or remove the allowBuilds block if onlyBuiltDependencies is the intended policy source. Leaving placeholders makes the workspace build policy ambiguous and carries forward the prior review finding.

Comment on lines +21 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] Resolve allowBuilds placeholders

These literal placeholder strings are still committed under allowBuilds. pnpm expects concrete boolean approvals here; leaving set this to true or false makes the build-script policy ambiguous and can break strict install/config validation. Since these packages are already listed in onlyBuiltDependencies, set the corresponding approvals explicitly.

Suggested change
'@biomejs/biome': set this to true or false
'@parcel/watcher': set this to true or false
cpu-features: set this to true or false
esbuild: set this to true or false
msgpackr-extract: set this to true or false
protobufjs: set this to true or false
sqlite3: set this to true or false
ssh2: set this to true or false
'@biomejs/biome': true
'@parcel/watcher': true
cpu-features: true
esbuild: true
msgpackr-extract: true
protobufjs: true
sqlite3: true
ssh2: true

76 changes: 76 additions & 0 deletions ts-tests/suites/zombienet_evm/00-evm-substrate-transfer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { beforeAll, describeSuite, expect } from "@moonwall/cli";
import { subtensor } from "@polkadot-api/descriptors";
import { ethers } from "ethers";
import type { TypedApi } from "polkadot-api";
import { convertH160ToSS58, forceSetBalance, raoToEth, tao, waitForFinalizedBlocks } from "../../utils";

function createEthersWallet(provider: ethers.JsonRpcProvider): ethers.Wallet {
const account = ethers.Wallet.createRandom();
return new ethers.Wallet(account.privateKey, provider);
}

async function estimateTransactionCost(provider: ethers.Provider, tx: ethers.TransactionRequest): Promise<bigint> {
const feeData = await provider.getFeeData();
const estimatedGas = await provider.estimateGas(tx);
const gasPrice = feeData.gasPrice ?? feeData.maxFeePerGas;
if (gasPrice == null) {
return estimatedGas;
}
return estimatedGas * gasPrice;
}

describeSuite({
id: "evm-substrate-transfer-basic",
title: "Basic EVM-Substrate Transfer Tests",
foundationMethods: "zombie",
testCases: ({ it, context }) => {
let api: TypedApi<typeof subtensor>;
let ethWallet: ethers.Wallet;
let ethWallet2: ethers.Wallet;

beforeAll(async () => {
api = context.papi("Node").getTypedApi(subtensor);

const provider = context.ethers("EVM").provider as ethers.JsonRpcProvider;
ethWallet = createEthersWallet(provider);
ethWallet2 = createEthersWallet(provider);

await forceSetBalance(api, convertH160ToSS58(ethWallet.address));
await forceSetBalance(api, convertH160ToSS58(ethWallet2.address));
await waitForFinalizedBlocks(api, 1);
}, 120000);

it({
id: "T01",
title: "Can transfer token from EVM to EVM",
test: async () => {
const provider = ethWallet.provider;
if (provider == null) {
throw new Error("ethWallet has no provider");
}

const senderBalanceBefore = await provider.getBalance(ethWallet.address);
const receiverBalanceBefore = await provider.getBalance(ethWallet2.address);

const transferAmount = raoToEth(tao(1));
const tx: ethers.TransactionRequest = {
to: ethWallet2.address,
value: transferAmount,
};

const txFee = await estimateTransactionCost(provider, tx);

const txResponse = await ethWallet.sendTransaction(tx);
const receipt = await txResponse.wait();
expect(receipt).toBeDefined();
expect(receipt!.status).toEqual(1);

const senderBalanceAfter = await provider.getBalance(ethWallet.address);
const receiverBalanceAfter = await provider.getBalance(ethWallet2.address);

expect(senderBalanceAfter).toEqual(senderBalanceBefore - transferAmount - txFee);
expect(receiverBalanceAfter).toEqual(receiverBalanceBefore + transferAmount);
},
});
},
});
28 changes: 28 additions & 0 deletions ts-tests/utils/address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { hexToU8a } from "@polkadot/util";
import { blake2AsU8a, encodeAddress } from "@polkadot/util-crypto";

const SS58_PREFIX = 42;

export function convertH160ToPublicKey(ethAddress: string) {
const prefix = "evm:";
const prefixBytes = new TextEncoder().encode(prefix);
const addressBytes = hexToU8a(ethAddress.startsWith("0x") ? ethAddress : `0x${ethAddress}`);
const combined = new Uint8Array(prefixBytes.length + addressBytes.length);

// Concatenate prefix and Ethereum address
combined.set(prefixBytes);
combined.set(addressBytes, prefixBytes.length);

// Hash the combined data (the public key)
const hash = blake2AsU8a(combined);
return hash;
}

export function convertH160ToSS58(ethAddress: string) {
// get the public key
const hash = convertH160ToPublicKey(ethAddress);

// Convert the hash to SS58 format
const ss58Address = encodeAddress(hash, SS58_PREFIX);
return ss58Address;
}
5 changes: 5 additions & 0 deletions ts-tests/utils/balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export function tao(value: number): bigint {
return TAO * BigInt(value);
}

/** Convert RAO to the EVM native balance unit (1 RAO → 1 gwei on-chain). */
export function raoToEth(rao: bigint): bigint {
return TAO * rao;
}

export async function getBalance(api: TypedApi<typeof subtensor>, ss58Address: string): Promise<bigint> {
const account = await api.query.System.Account.getValue(ss58Address);
return account.data.free;
Expand Down
12 changes: 7 additions & 5 deletions ts-tests/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export * from "./transactions.js";
export * from "./balance.js";
export * from "./subnet.js";
export * from "./staking.js";
export * from "./shield_helpers.ts";
export * from "./account.ts";
export * from "./address.ts";
export * from "./balance.js";
export * from "./coldkey_swap.ts";
export * from "./config.js";
export * from "./shield_helpers.ts";
export * from "./staking.js";
export * from "./subnet.js";
export * from "./transactions.js";
Loading