diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index d4a85d80..13650c28 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -23,5 +23,21 @@ jobs: - run: npm ci + # Smoke tests shell out to `node dist/index.js` to exercise the + # compiled CLI — without a build first, that file doesn't exist + # and the `genlayer staking validators` assertion fails with + # MODULE_NOT_FOUND before it can hit the network at all. + - name: Build CLI + run: npm run build + + # The staking subcommands need a keystore for the default account + # even for read-only listings (BaseAction loads the account to + # report "your stake"). Create a throwaway one with a known + # password so the subprocess test in tests/smoke.test.ts can run. + - name: Create default keystore for CLI subprocess tests + run: node dist/index.js account create --name default --password ci-smoke-test + env: + CI: true + - name: Run smoke tests run: npm run test:smoke diff --git a/package-lock.json b/package-lock.json index 4fab6514..b642ca1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "dotenv": "^17.0.0", "ethers": "^6.13.4", "fs-extra": "^11.3.0", - "genlayer-js": "^0.28.5", + "genlayer-js": "^1.0.0", "inquirer": "^12.0.0", "keytar": "^7.9.0", "node-fetch": "^3.0.0", @@ -5581,9 +5581,9 @@ } }, "node_modules/genlayer-js": { - "version": "0.28.5", - "resolved": "https://registry.npmjs.org/genlayer-js/-/genlayer-js-0.28.5.tgz", - "integrity": "sha512-Wj4tsqvywV9EZtJgp5MeDQcFzc18FXK3x+2ihAGqkY9SB8g0BC39ihj+ysQNHXUCiFmTK4hy3DwRzeWiZf1jRg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/genlayer-js/-/genlayer-js-1.0.0.tgz", + "integrity": "sha512-372szMXY95jI32u7xOTrqHOXk3Wf4WdsrmXSzgXywv7TBSRwj/W3W4s9oBBpzMIxUzS06Bv80rTCrblvuH78ug==", "license": "MIT", "dependencies": { "eslint-plugin-import": "^2.30.0", diff --git a/package.json b/package.json index d0356190..958b05e3 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "dotenv": "^17.0.0", "ethers": "^6.13.4", "fs-extra": "^11.3.0", - "genlayer-js": "^0.28.5", + "genlayer-js": "^1.0.0", "inquirer": "^12.0.0", "keytar": "^7.9.0", "node-fetch": "^3.0.0", diff --git a/src/commands/account/send.ts b/src/commands/account/send.ts index 66188bc8..716d577b 100644 --- a/src/commands/account/send.ts +++ b/src/commands/account/send.ts @@ -31,18 +31,20 @@ export class SendAction extends BaseAction { } private parseAmount(amount: string): bigint { - // Support "10gen" or "10" (assumes gen) or wei values - const lowerAmount = amount.toLowerCase(); - if (lowerAmount.endsWith("gen")) { - const value = lowerAmount.slice(0, -3); - return parseEther(value); + // Symmetric with genlayer-js `parseStakingAmount`: + // "Ngen" → N GEN (parseEther) + // integer → wei (as-is) + // decimal without suffix → rejected (ambiguous; was previously silently + // interpreted as GEN for small values and as wei for large — a footgun + // that made `send 1000` attempt to transfer 1000 GEN, not 1000 wei). + const trimmed = amount.trim(); + const lower = trimmed.toLowerCase(); + if (lower.endsWith("gen")) { + return parseEther(lower.slice(0, -3).trim()); } - // If it's a large number (likely wei), use as-is - if (BigInt(amount) > 1_000_000_000_000n) { - return BigInt(amount); - } - // Otherwise assume it's in GEN - return parseEther(amount); + // Plain integer → wei. BigInt() throws on decimals, which is the intended + // failure mode for ambiguous input like "1.5". + return BigInt(trimmed); } async execute(options: SendOptions): Promise { diff --git a/tests/smoke.test.ts b/tests/smoke.test.ts index f9dfb7d3..56808871 100644 --- a/tests/smoke.test.ts +++ b/tests/smoke.test.ts @@ -7,7 +7,10 @@ import type {Address, GenLayerChain} from "genlayer-js/types"; const CLI = path.resolve(__dirname, "../dist/index.js"); -const TIMEOUT = 30_000; +// Testnet validator-list fetches ALL validators + per-validator detail in +// batches; on bradbury/asimov that routinely passes 30s. 90s gives headroom +// without hiding real hangs. +const TIMEOUT = 90_000; const testnets: {name: string; chain: GenLayerChain}[] = [ {name: "Asimov", chain: testnetAsimov},