mirror of
https://github.com/luxfi/bank.git
synced 2026-10-30 17:02:07 +00:00
The visual swarm confirmed the sandbox + testnet crypto loop is
genuinely live end-to-end (12/12 Playwright specs, 12 screenshots)
over a real double-entry ledger — and flagged that the chain boundary
is simulated and mislabeled. Fixes:
- Live-mode crypto send now 503s ("on-chain send unavailable") instead
of debiting the ledger against a random txHash that is never
broadcast — the same discipline the exchange applies without forexd.
The faucet was already sandbox-gated; send was not.
- Network label is derived from mode via networkName() (lux-testnet /
lux-mainnet), not hardcoded; the wallet and crypto-prices responses
report sandbox: Sandbox() instead of a literal true, so a production
deploy can no longer report itself as testnet.
- getMonthlyVolume normalizes to USD cents (was summing raw minor
units, letting crypto micro-units inflate volume ~10^4x and grant
the fee discount by accident) — matching the limit/AML fix.
- Transaction history carries txHash + network from metadata, so the
activity feed can render a send's hash, not just the live response.
- Wallet drops the false "Non-custodial" label (the sandbox wallet is
custodial with an unspendable display address).
Also lands the swarm's visual e2e suite (crypto-loop, dashboard,
exchange specs + screenshots on every flow) and the agent's fix to
Exchange.tsx, where the convert confirmation was wiped on the same
tick it was set — a customer never saw "Converted X to Y".
Co-authored-by: Hanzo Dev <[email protected]>
38 lines
1.6 KiB
TypeScript
38 lines
1.6 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { signIn, holding, holdingAmount, shot } from './demo'
|
|
|
|
// An EIP-55 checksummed address — the send path verifies the checksum, so this
|
|
// is the shape a customer's wallet actually hands over.
|
|
const CHECKSUMMED = '0x52908400098527886E0F7030069857D2E4169EE7'
|
|
|
|
test('faucet, hold, send: the whole crypto loop in one pass', async ({ page }) => {
|
|
await signIn(page)
|
|
await page.getByRole('link', { name: 'Wallet' }).first().click()
|
|
await expect(page.getByRole('heading', { name: 'Crypto wallet' })).toBeVisible()
|
|
|
|
// Receive: draw 1 ETH from the testnet faucet.
|
|
const before = (await holdingAmount(page, 'ETH')) ?? 0
|
|
await page.getByRole('button', { name: 'Receive', exact: true }).click()
|
|
await page.getByRole('button', { name: '+1 ETH' }).click()
|
|
|
|
await expect(holding(page, 'ETH')).toBeVisible()
|
|
await expect
|
|
.poll(() => holdingAmount(page, 'ETH'), { message: 'faucet should credit the ETH holding' })
|
|
.toBeGreaterThan(before)
|
|
const funded = (await holdingAmount(page, 'ETH')) as number
|
|
|
|
// Send: 0.1 ETH out to a checksummed destination.
|
|
await page.getByRole('button', { name: 'Send', exact: true }).click()
|
|
await page.getByRole('combobox').selectOption('ETH')
|
|
await page.getByPlaceholder('Amount').fill('0.1')
|
|
await page.getByPlaceholder('Destination address').fill(CHECKSUMMED)
|
|
await page.getByRole('button', { name: 'Send ETH' }).click()
|
|
|
|
await expect(page.getByText(/Sent · 0x[0-9a-f]{64}/)).toBeVisible()
|
|
await expect
|
|
.poll(() => holdingAmount(page, 'ETH'), { message: 'the send should debit the ETH holding' })
|
|
.toBeLessThan(funded)
|
|
|
|
await shot(page, 'crypto-loop')
|
|
})
|