Files
universe/e2e/11-wallet-ops.spec.ts
Hanzo Dev 548440d94f k8s(financial): bump lux-financial to v1.2.0
Expanded solutions (10 new industries) and products (Compliance & Data
section) to cover full docs.lux.financial capability surface.
2026-04-20 22:33:19 -07:00

187 lines
8.5 KiB
TypeScript

import { test, expect } from '@playwright/test'
import {
getUrls,
rpcCall, ethBlockNumber,
httpStatus,
} from './helpers'
const urls = getUrls()
// ---------------------------------------------------------------------------
// Wallet Operations — RPC-level address and transaction queries
// ---------------------------------------------------------------------------
// ── Address Format Validation ──────────────────────────────────────
test.describe('Wallet Ops — Address Format Validation', () => {
test('C-Chain returns valid hex balance for 0x address', async ({ request }) => {
const result = await rpcCall(request, urls.rpc, 'eth_getBalance', [
'0x1111111111111111111111111111111111111111', 'latest',
]) as string
expect(result).toMatch(/^0x[0-9a-fA-F]+$/)
console.log(` C-Chain balance (0x1111...): ${result}`)
})
test('X-Chain address format starts with X-', async ({ request }) => {
// Verify X-Chain is accessible; address formatting is client-side
// but we confirm X-Chain RPC responds with valid data
const result = await rpcCall(
request, urls.rpcPlatform, 'platform.getBlockchains', {},
) as { blockchains: { name: string; id: string; subnetID: string }[] }
const xChain = result.blockchains.find(c => c.name === 'X-Chain')
expect(xChain).toBeDefined()
console.log(` X-Chain ID: ${xChain!.id}`)
// X-Chain addresses are: X-lux1... (Bech32 with X- prefix)
})
test('P-Chain address format starts with P-', async ({ request }) => {
// Verify P-Chain validators have NodeID-prefixed IDs
const result = await rpcCall(
request, urls.rpcPlatform, 'platform.getCurrentValidators', { subnetID: '' },
) as { validators: { nodeID: string }[] }
expect(result.validators.length).toBeGreaterThan(0)
for (const v of result.validators) {
expect(v.nodeID).toMatch(/^NodeID-/)
}
console.log(` P-Chain validator IDs all start with NodeID-`)
// P-Chain addresses are: P-lux1... (Bech32 with P- prefix)
})
})
// ── Gas Estimation ─────────────────────────────────────────────────
test.describe('Wallet Ops — Gas Estimation', () => {
test('eth_estimateGas returns valid estimate for simple transfer', async ({ request }) => {
const result = await rpcCall(request, urls.rpc, 'eth_estimateGas', [
{
from: '0x0000000000000000000000000000000000000001',
to: '0x0000000000000000000000000000000000000002',
value: '0x0',
},
]) as string
const gas = parseInt(result, 16)
console.log(` Estimated gas (simple transfer): ${gas}`)
// Standard transfer should cost ~21000 gas
expect(gas).toBeGreaterThanOrEqual(21000)
expect(gas).toBeLessThan(100_000)
})
test('eth_estimateGas returns higher estimate for contract interaction', async ({ request }) => {
// Simulate a call with data (contract interaction)
try {
const result = await rpcCall(request, urls.rpc, 'eth_estimateGas', [
{
from: '0x0000000000000000000000000000000000000001',
to: '0x0000000000000000000000000000000000000002',
data: '0xa9059cbb0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a',
},
]) as string
const gas = parseInt(result, 16)
console.log(` Estimated gas (with data): ${gas}`)
expect(gas).toBeGreaterThanOrEqual(21000)
} catch {
// Reverted calls are acceptable — the estimate RPC itself works
console.log(' Contract call reverted (expected for non-contract address)')
}
})
})
// ── Nonce Queries ──────────────────────────────────────────────────
test.describe('Wallet Ops — Nonce Queries', () => {
test('eth_getTransactionCount returns valid nonce for zero address', async ({ request }) => {
const result = await rpcCall(request, urls.rpc, 'eth_getTransactionCount', [
'0x0000000000000000000000000000000000000000', 'latest',
]) as string
const nonce = parseInt(result, 16)
console.log(` Nonce (zero addr): ${nonce}`)
expect(nonce).toBeGreaterThanOrEqual(0)
})
test('eth_getTransactionCount works for pending state', async ({ request }) => {
const result = await rpcCall(request, urls.rpc, 'eth_getTransactionCount', [
'0x0000000000000000000000000000000000000001', 'pending',
]) as string
const nonce = parseInt(result, 16)
console.log(` Nonce (0x01, pending): ${nonce}`)
expect(nonce).toBeGreaterThanOrEqual(0)
})
})
// ── Block and Receipt Queries ──────────────────────────────────────
test.describe('Wallet Ops — Block and Receipt Queries', () => {
test('eth_getBlockByNumber returns valid block', async ({ request }) => {
const result = await rpcCall(request, urls.rpc, 'eth_getBlockByNumber', [
'latest', false,
]) as { number: string; hash: string; timestamp: string; gasUsed: string }
expect(result).toBeDefined()
expect(result.number).toMatch(/^0x/)
expect(result.hash).toMatch(/^0x[0-9a-fA-F]{64}$/)
const blockNum = parseInt(result.number, 16)
console.log(` Latest block: ${blockNum}, hash: ${result.hash.slice(0, 18)}...`)
expect(blockNum).toBeGreaterThanOrEqual(0)
})
test('eth_getBlockByNumber with full txs returns transactions array', async ({ request }) => {
const result = await rpcCall(request, urls.rpc, 'eth_getBlockByNumber', [
'latest', true,
]) as { transactions: unknown[] }
expect(result).toBeDefined()
expect(Array.isArray(result.transactions)).toBe(true)
console.log(` Transactions in latest block: ${result.transactions.length}`)
})
test('eth_getCode returns empty for EOA', async ({ request }) => {
const result = await rpcCall(request, urls.rpc, 'eth_getCode', [
'0x0000000000000000000000000000000000000001', 'latest',
]) as string
// EOA or precompile — should return 0x or some bytecode
expect(result).toBeDefined()
console.log(` Code at 0x01: ${result.slice(0, 20)}${result.length > 20 ? '...' : ''}`)
})
})
// ── Account Storage ────────────────────────────────────────────────
test.describe('Wallet Ops — Storage Queries', () => {
test('eth_getStorageAt returns valid slot', async ({ request }) => {
const result = await rpcCall(request, urls.rpc, 'eth_getStorageAt', [
'0x0000000000000000000000000000000000000000',
'0x0',
'latest',
]) as string
expect(result).toMatch(/^0x/)
console.log(` Storage slot 0 at zero addr: ${result}`)
})
})
// ── Fee History ────────────────────────────────────────────────────
test.describe('Wallet Ops — Fee History (EIP-1559)', () => {
test('eth_feeHistory returns base fee data', async ({ request }) => {
try {
const result = await rpcCall(request, urls.rpc, 'eth_feeHistory', [
'0x4', 'latest', [25, 75],
]) as { baseFeePerGas: string[]; gasUsedRatio: number[]; oldestBlock: string }
expect(result.baseFeePerGas).toBeDefined()
expect(result.baseFeePerGas.length).toBeGreaterThan(0)
expect(result.gasUsedRatio).toBeDefined()
const oldestBlock = parseInt(result.oldestBlock, 16)
console.log(` Fee history oldest block: ${oldestBlock}`)
console.log(` Base fees: ${result.baseFeePerGas.map(f => parseInt(f, 16)).join(', ')}`)
} catch {
// eth_feeHistory may not be supported on all chains
console.log(' eth_feeHistory not supported (pre-EIP-1559 chain)')
}
})
})
// ── Max Priority Fee ───────────────────────────────────────────────
test.describe('Wallet Ops — Max Priority Fee', () => {
test('eth_maxPriorityFeePerGas returns a value', async ({ request }) => {
try {
const result = await rpcCall(request, urls.rpc, 'eth_maxPriorityFeePerGas') as string
const fee = BigInt(result)
console.log(` maxPriorityFeePerGas: ${Number(fee) / 1e9} gwei`)
expect(fee).toBeGreaterThanOrEqual(0n)
} catch {
console.log(' eth_maxPriorityFeePerGas not supported')
}
})
})