Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
88 changes: 88 additions & 0 deletions product-sdk/e2e-support/check-product-accounts.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Reports the balance of every product account a live-chain suite signs with.
//
// A product account is derived from (session root, product id), so it is its own
// account and starts empty. A drained one fails as `Invalid.Payment` before
// inclusion, or `Statement submission rejected: {}` from the statement store,
// which weights acceptance by balance. Both read as protocol faults, so naming
// the address and its balance here is what turns one into an errand.
//
// Usage: node e2e-support/check-product-accounts.mjs [--rpc <url>]
import { Twox128, Blake2128Concat, AccountId } from "@polkadot-api/substrate-bindings";

const RPC =
process.argv[process.argv.indexOf("--rpc") + 1]?.startsWith("ws")
? process.argv[process.argv.indexOf("--rpc") + 1]
: "wss://paseo-asset-hub-next-rpc.polkadot.io";

// The demos whose fixture proxies to a live chain, and the account each
// activates its session from (the first its fixture lists).
export const LIVE_CHAIN_ACCOUNTS = [
{ demo: "tx-demo", address: "5EEoZCuLSwtnbaJPEubsr6smMWsAoNW1Hv5omUGgQyM5zYEU" },
{ demo: "contracts-demo", address: "5Dkio4JAknT6B2T23UggdjwTeprFZZd8ijqQdzejuVcC5Scy" },
{ demo: "statement-store-demo", address: "5HpYZBoT3qSzXp5yHp1Xk5pdB2cRENFFeaq76yej51gefiTH" },
];

const hex = (bytes) => "0x" + Buffer.from(bytes).toString("hex");
const label = new TextEncoder();

/** The `System.Account` storage key for an address. */
function accountKey(address) {
return hex([
...Twox128(label.encode("System")),
...Twox128(label.encode("Account")),
...Blake2128Concat(AccountId().enc(address)),
]);
}

/** Free balance in planck, or `undefined` when the account is not on chain. */
export async function freeBalance(call, address) {
const raw = await call("state_getStorage", [accountKey(address)]);
if (!raw) return undefined;
const bytes = Buffer.from(raw.slice(2), "hex");
// AccountInfo: nonce u32, consumers u32, providers u32, sufficients u32, then
// AccountData whose first field is the free balance, a u128.
return bytes.readBigUInt64LE(16) + (bytes.readBigUInt64LE(24) << 64n);
}

async function main() {
const socket = new WebSocket(RPC);
const pending = new Map();
let id = 1;
const call = (method, params = []) =>
new Promise((resolve, reject) => {
const n = id++;
pending.set(n, { resolve, reject });
socket.send(JSON.stringify({ jsonrpc: "2.0", id: n, method, params }));
setTimeout(() => reject(new Error(`${method} timed out`)), 20_000);
});
socket.onmessage = (event) => {
const message = JSON.parse(event.data);
const waiter = pending.get(message.id);
if (!waiter) return;
pending.delete(message.id);
message.error ? waiter.reject(new Error(message.error.message)) : waiter.resolve(message.result);
};
await new Promise((resolve, reject) => {
socket.onopen = resolve;
socket.onerror = () => reject(new Error(`cannot reach ${RPC}`));
});

console.log(`${await call("system_chain")} (${RPC})\n`);
const empty = [];
for (const target of LIVE_CHAIN_ACCOUNTS) {
const free = await freeBalance(call, target.address);
const state =
free === undefined ? "NOT ON CHAIN" : free === 0n ? "EMPTY" : `${(Number(free) / 1e10).toFixed(4)}`;
if (free === undefined || free === 0n) empty.push(target);
console.log(` ${target.demo.padEnd(22)} ${state.padEnd(14)} ${target.address}`);
}
socket.close();

if (empty.length) {
console.log(`\n${empty.length} account(s) need funding before a write test can pass:`);
for (const target of empty) console.log(` ${target.address} (${target.demo})`);
process.exitCode = 1;
}
}

if (import.meta.url === `file://${process.argv[1]}`) await main();
53 changes: 16 additions & 37 deletions product-sdk/examples/chain-client-demo/e2e/fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,27 @@
// Copyright 2026 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
//
// TrUAPI-native fixture. Only the source of the fixture changed; the specs and
// their assertions are untouched.
import { test as base } from "@playwright/test";
import {
createTestHostFixture,
PASEO_ASSET_HUB,
type HexString,
type NetworkConfig,
type TestHost,
} from "@parity/host-api-test-sdk/playwright";
import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
} from "@parity/truapi-host/testing/playwright";
import { LIVE_CHAINS, liveChain } from "@parity/truapi-host/testing/dev-accounts";

// Paseo Asset Hub uses SS58 prefix 0 -> addresses start with "1".
export const SS58_PREFIX = 0;
const PRODUCT_URL = "http://localhost:5260";

/**
* Paseo Asset Hub config with a configurable RPC endpoint.
*
* The genesis comes from the descriptor because the test SDK's constant lags
* chain resets, and a stale one makes the host reject the chain.
*
* Override via `PASEO_AH_RPC` if the default RPC has outages. The override must
* serve the same chain as the descriptor's genesis; a mirror on any other
* genesis fails the chain handshake (seen as `Tracking stopped` / `BadProof`).
*/
const PASEO_AH: NetworkConfig = {
...PASEO_ASSET_HUB,
genesisHash: paseo_asset_hub.genesis as HexString,
rpcUrl: process.env.PASEO_AH_RPC ?? "wss://paseo-asset-hub-next-rpc.polkadot.io",
};

/**
* Default fixture: Bob on Paseo Asset Hub.
*
* `productAccounts` maps this app's DotNS-derived account (used by `SignerManager`
* when it asks the host for a non-product account) to the funded dev keypair.
*/
const bobFixture = createTestHostFixture({
productUrl: PRODUCT_URL,
productId: "chain-client-demo.dot",
accounts: ["bob"],
networks: [PASEO_AH],
productAccounts: { "chain-client-demo.dot": "bob" },
});

export const test = base.extend<{ testHost: TestHost }>(bobFixture);
export const test = base.extend<{ testHost: TestHost }>(
createTestHostFixture({
productUrl: PRODUCT_URL,
productId: "chain-client-demo.dot",
accounts: ["alice", "bob", "charlie", "dave"],
// Reads real chain state, so the host proxies to the real Asset Hub.
// `liveChain` keeps the proxy, the reported chain set and the runtime
// config's genesis in agreement; the product checks the last two.
...liveChain(LIVE_CHAINS.paseoAssetHub),
}),
);
export { expect } from "@playwright/test";
2 changes: 1 addition & 1 deletion product-sdk/examples/chain-client-demo/e2e/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright 2026 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
import type { TestHost } from "@parity/host-api-test-sdk/playwright";
import type { TestHost } from "@parity/truapi-host/testing/playwright";
import { expect, type FrameLocator } from "@playwright/test";

/**
Expand Down
6 changes: 3 additions & 3 deletions product-sdk/examples/chain-client-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.1",
"private": true,
"type": "module",
"description": "E2E demo app for @parity/product-sdk-chain-client — exercises preset and BYOD chain connections via the Host API path",
"description": "E2E demo app for @parity/product-sdk-chain-client \u2014 exercises preset and BYOD chain connections via the Host API path",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
Expand All @@ -20,9 +20,9 @@
"polkadot-api": "catalog:"
},
"devDependencies": {
"@parity/host-api-test-sdk": "catalog:",
"@playwright/test": "catalog:",
"typescript": "catalog:",
"vite": "catalog:"
"vite": "catalog:",
"@parity/truapi-host": "catalog:"
}
}
39 changes: 11 additions & 28 deletions product-sdk/examples/cloud-storage-demo/e2e/fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,22 @@
// Copyright 2026 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
//
// TrUAPI-native fixture. Only the source of the fixture changed; the specs and
// their assertions are untouched.
import { test as base } from "@playwright/test";
import {
createTestHostFixture,
PASEO_ASSET_HUB,
type HexString,
type NetworkConfig,
type TestHost,
} from "@parity/host-api-test-sdk/playwright";
import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
} from "@parity/truapi-host/testing/playwright";

export const SS58_PREFIX = 0;
const PRODUCT_URL = "http://localhost:5230";

/**
* Paseo Asset Hub config for account resolution. The preimage manager
* host API is independent of the chain connection — it's a separate
* protocol. The bulletin chain itself connects via direct WS fallback.
*
* The genesis comes from the descriptor because the test SDK's constant lags
* chain resets, and a stale one makes the host reject the chain.
*/
const PASEO_AH: NetworkConfig = {
...PASEO_ASSET_HUB,
genesisHash: paseo_asset_hub.genesis as HexString,
rpcUrl: process.env.PASEO_AH_RPC ?? "wss://paseo-asset-hub-next-rpc.polkadot.io",
};

const bobFixture = createTestHostFixture({
productUrl: PRODUCT_URL,
productId: "bulletin-demo.dot",
accounts: ["bob"],
networks: [PASEO_AH],
productAccounts: { "bulletin-demo.dot": "bob" },
});

export const test = base.extend<{ testHost: TestHost }>(bobFixture);
export const test = base.extend<{ testHost: TestHost }>(
createTestHostFixture({
productUrl: PRODUCT_URL,
productId: "cloud-storage-demo.dot",
accounts: ["alice", "bob", "charlie", "dave"],
}),
);
export { expect } from "@playwright/test";
2 changes: 1 addition & 1 deletion product-sdk/examples/cloud-storage-demo/e2e/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright 2026 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
import type { TestHost } from "@parity/host-api-test-sdk/playwright";
import type { TestHost } from "@parity/truapi-host/testing/playwright";
import { expect, type FrameLocator } from "@playwright/test";

/**
Expand Down
6 changes: 3 additions & 3 deletions product-sdk/examples/cloud-storage-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.1",
"private": true,
"type": "module",
"description": "E2E demo app for @parity/product-sdk-cloud-storage — exercises CloudStorageClient upload and query via the Host API preimage path",
"description": "E2E demo app for @parity/product-sdk-cloud-storage \u2014 exercises CloudStorageClient upload and query via the Host API preimage path",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
Expand All @@ -20,9 +20,9 @@
"polkadot-api": "catalog:"
},
"devDependencies": {
"@parity/host-api-test-sdk": "catalog:",
"@playwright/test": "catalog:",
"typescript": "catalog:",
"vite": "catalog:"
"vite": "catalog:",
"@parity/truapi-host": "catalog:"
}
}
44 changes: 14 additions & 30 deletions product-sdk/examples/contracts-demo/e2e/fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,25 @@
// Copyright 2026 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
//
// TrUAPI-native fixture. Only the source of the fixture changed; the specs and
// their assertions are untouched.
import { test as base } from "@playwright/test";
import {
createTestHostFixture,
PASEO_ASSET_HUB,
type HexString,
type NetworkConfig,
type TestHost,
} from "@parity/host-api-test-sdk/playwright";
import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
} from "@parity/truapi-host/testing/playwright";
import { LIVE_CHAINS, liveChain } from "@parity/truapi-host/testing/dev-accounts";

export const SS58_PREFIX = 0; // Paseo Asset Hub — addresses start with "1"
const PRODUCT_URL = "http://localhost:5201";

/**
* Paseo Asset Hub config with a configurable RPC endpoint.
*
* The genesis comes from the descriptor because the test SDK's constant lags
* chain resets, and a stale one makes the host reject the chain.
*
* Override via `PASEO_AH_RPC` if the default RPC has outages. The override must
* serve the same chain as the descriptor's genesis; a mirror on any other
* genesis fails the chain handshake (seen as `Tracking stopped` / `BadProof`).
*/
const PASEO_AH: NetworkConfig = {
...PASEO_ASSET_HUB,
genesisHash: paseo_asset_hub.genesis as HexString,
rpcUrl: process.env.PASEO_AH_RPC ?? "wss://paseo-asset-hub-next-rpc.polkadot.io",
};

const bobFixture = createTestHostFixture({
productUrl: PRODUCT_URL,
productId: "contracts-demo.dot",
accounts: ["bob"],
networks: [PASEO_AH],
productAccounts: { "contracts-demo.dot": "bob" },
});

export const test = base.extend<{ testHost: TestHost }>(bobFixture);
export const test = base.extend<{ testHost: TestHost }>(
createTestHostFixture({
productUrl: PRODUCT_URL,
productId: "contracts-demo.dot",
accounts: ["alice", "bob", "charlie", "dave"],
// Reads real contract state, so proxy to the real Asset Hub.
...liveChain(LIVE_CHAINS.paseoAssetHub),
}),
);
export { expect } from "@playwright/test";
2 changes: 1 addition & 1 deletion product-sdk/examples/contracts-demo/e2e/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright 2026 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
import type { TestHost } from "@parity/host-api-test-sdk/playwright";
import type { TestHost } from "@parity/truapi-host/testing/playwright";
import { expect, type FrameLocator } from "@playwright/test";

/**
Expand Down
6 changes: 3 additions & 3 deletions product-sdk/examples/contracts-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.1",
"private": true,
"type": "module",
"description": "E2E demo for @parity/product-sdk-contracts — exercises ContractManager query/tx via the Host API path",
"description": "E2E demo for @parity/product-sdk-contracts \u2014 exercises ContractManager query/tx via the Host API path",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
Expand All @@ -20,9 +20,9 @@
"polkadot-api": "catalog:"
},
"devDependencies": {
"@parity/host-api-test-sdk": "catalog:",
"@playwright/test": "catalog:",
"typescript": "catalog:",
"vite": "catalog:"
"vite": "catalog:",
"@parity/truapi-host": "catalog:"
}
}
Loading
Loading