Skip to content
Closed
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
63 changes: 63 additions & 0 deletions .github/workflows/ts-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: TypeScript CI

on:
pull_request:
branches:
- "**"
push:
branches:
- "**"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ts-ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
quality:
name: Quality / Node ${{ matrix.node }}
runs-on: ubuntu-24.04
timeout-minutes: 20

strategy:
fail-fast: false
matrix:
node:
- "22"
- "24"

defaults:
run:
working-directory: ts

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
cache: npm
cache-dependency-path: ts/package-lock.json

- name: Install locked dependencies
run: npm ci

- name: Check architecture boundaries
run: npm run depcruise

- name: Type-check
run: npm run typecheck

- name: Run tests
run: npm test

- name: Build npm bundle
run: npm run build

- name: Validate npm package contents
run: npm pack --dry-run
228 changes: 228 additions & 0 deletions .github/workflows/ts-standalone-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
name: TypeScript Standalone Artifacts

on:
push:
branches:
- master
workflow_dispatch:

permissions:
contents: read

env:
NODE_VERSION: "20"

jobs:
quality:
name: TypeScript quality gates
runs-on: ubuntu-24.04
defaults:
run:
working-directory: ts

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
cache-dependency-path: ts/package-lock.json

- name: Install dependencies
run: npm ci

- name: Check architecture boundaries
run: npm run depcruise

- name: Type-check
run: npm run typecheck

- name: Test
run: npm test

- name: Build npm bundle
run: npm run build

binaries:
name: ${{ matrix.asset }}
needs: quality
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
target: bun-linux-x64-baseline
asset: linux-x64
executable: wallet-cli
archive: tar.gz
glibc_max: "2.35"
- os: ubuntu-22.04-arm
target: bun-linux-arm64
asset: linux-arm64
executable: wallet-cli
archive: tar.gz
glibc_max: "2.35"
- os: macos-15-intel
target: bun-darwin-x64
asset: macos-x64
executable: wallet-cli
archive: tar.gz
- os: macos-15
target: bun-darwin-arm64
asset: macos-arm64
executable: wallet-cli
archive: tar.gz
- os: windows-2022
target: bun-windows-x64-baseline
asset: windows-x64
executable: wallet-cli.exe
archive: zip
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: ts

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
cache-dependency-path: ts/package-lock.json

- name: Install dependencies
run: npm ci

- name: Build standalone executable
run: >-
npm run build:standalone --
--target ${{ matrix.target }}
--outfile standalone/${{ matrix.executable }}

- name: Verify executable and embedded Ledger addon
run: node scripts/verify-standalone.mjs standalone/${{ matrix.executable }}

- name: Verify Linux ABI baseline
if: runner.os == 'Linux'
shell: bash
env:
MAX_GLIBC: ${{ matrix.glibc_max }}
run: |
set -euo pipefail
executable="standalone/${{ matrix.executable }}"
native_addon="$(
./node_modules/.bin/bun -e \
'import { resolveNodeHidAddon } from "./scripts/standalone/resolve-node-hid-addon.ts"; console.log(resolveNodeHidAddon().nativeAddon)'
)"
host_glibc="$(getconf GNU_LIBC_VERSION | awk '{print $2}')"

if [[ "${host_glibc}" != "${MAX_GLIBC}" ]]; then
echo "::error::runner glibc ${host_glibc} does not match the supported baseline ${MAX_GLIBC}"
exit 1
fi

required_versions="$(
{
readelf --version-info --wide "${executable}"
readelf --version-info --wide "${native_addon}"
} | grep -oE 'GLIBC_[0-9]+(\.[0-9]+)*' | sed 's/^GLIBC_//' | sort -Vu
)"
highest_required="$(printf '%s\n' "${required_versions}" | tail -n 1)"
highest_version="$(printf '%s\n%s\n' "${MAX_GLIBC}" "${highest_required}" | sort -V | tail -n 1)"

if [[ "${highest_version}" != "${MAX_GLIBC}" ]]; then
echo "::error::Linux artifact requires GLIBC_${highest_required}; maximum allowed is GLIBC_${MAX_GLIBC}"
exit 1
fi

dynamic_dependencies="$(readelf --dynamic --wide "${native_addon}")"
if ! grep -Fq 'Shared library: [libudev.so.1]' <<< "${dynamic_dependencies}"; then
echo "::error::Ledger native addon no longer declares its documented libudev.so.1 dependency"
exit 1
fi

echo "Verified GLIBC_${highest_required} <= GLIBC_${MAX_GLIBC} and libudev.so.1 dependency"

- name: Verify macOS code signature
if: runner.os == 'macOS'
run: codesign --verify --verbose standalone/${{ matrix.executable }}

- name: Package Unix archive
if: matrix.archive == 'tar.gz'
shell: bash
run: |
set -euo pipefail
version="$(node -p "require('./package.json').version")"
package="wallet-cli-${version}-${{ matrix.asset }}"
mkdir -p "standalone-assets/${package}"
cp "standalone/${{ matrix.executable }}" "standalone-assets/${package}/wallet-cli"
cp LICENSE "standalone-assets/${package}/LICENSE"
tar -C standalone-assets -czf "standalone-assets/${package}.tar.gz" "${package}"
rm -r "standalone-assets/${package}"

- name: Package Windows archive
if: matrix.archive == 'zip'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$version = node -p "require('./package.json').version"
$package = "wallet-cli-$version-${{ matrix.asset }}"
New-Item -ItemType Directory -Force "standalone-assets/$package" | Out-Null
Copy-Item "standalone/${{ matrix.executable }}" "standalone-assets/$package/wallet-cli.exe"
Copy-Item "LICENSE" "standalone-assets/$package/LICENSE"
Compress-Archive -Path "standalone-assets/$package" -DestinationPath "standalone-assets/$package.zip"
Remove-Item -Recurse "standalone-assets/$package"

- name: Upload platform artifact
uses: actions/upload-artifact@v4
with:
name: wallet-cli-${{ matrix.asset }}
path: ts/standalone-assets/*
if-no-files-found: error
retention-days: 1

artifacts:
name: Publish Actions artifact
needs: binaries
runs-on: ubuntu-24.04
permissions:
artifact-metadata: write
attestations: write
contents: read
id-token: write

steps:
- name: Download binaries
uses: actions/download-artifact@v4
with:
pattern: wallet-cli-*
path: standalone-assets
merge-multiple: true

- name: Create checksums and metadata
shell: bash
run: |
set -euo pipefail
cd standalone-assets
printf 'commit=%s\nref=%s\nrun_id=%s\n' \
"${GITHUB_SHA}" "${GITHUB_REF}" "${GITHUB_RUN_ID}" > BUILD_METADATA.txt
sha256sum BUILD_METADATA.txt wallet-cli-* > SHA256SUMS.txt

- name: Attest standalone archives
uses: actions/attest@v4
with:
subject-checksums: standalone-assets/SHA256SUMS.txt

- name: Upload standalone bundle
uses: actions/upload-artifact@v4
with:
name: wallet-cli-standalone-${{ github.sha }}
path: standalone-assets/*
if-no-files-found: error
retention-days: 30
4 changes: 2 additions & 2 deletions ts/docs/commands/account/activate.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ Requires the payer account and the master password via `--password-stdin`; watch
| `--dry-run` | Build and estimate only; no signature/broadcast, no password. Excludes `--sign-only` / `--build-only` |
| `--sign-only` | Build and sign, output the signed hex (feed [`tx broadcast`](../tx/broadcast.md)). Excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex (feed [`tx multisig --create`](../tx/multisig.md)). Excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only` |
| `--permission-id <n>` | Permission group to sign with (default `0`) |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
4 changes: 2 additions & 2 deletions ts/docs/commands/account/set.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Requires the account and the master password via `--password-stdin`; watch-only
| `--dry-run` | Build and estimate only; no signature/broadcast, no password. Excludes `--sign-only` / `--build-only` |
| `--sign-only` | Build and sign, output the signed hex (feed [`tx broadcast`](../tx/broadcast.md)). Excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex (feed [`tx multisig --create`](../tx/multisig.md)). Excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only` |
| `--permission-id <n>` | Permission group to sign with (default `0`) |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
2 changes: 1 addition & 1 deletion ts/docs/commands/contract/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Requires an account. The master password (via `--password-stdin`) is needed only
| `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 29=active); default `0` |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
2 changes: 1 addition & 1 deletion ts/docs/commands/contract/send.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Requires an account. The master password (via `--password-stdin`) is needed only
| `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 29=active); default `0` |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
4 changes: 2 additions & 2 deletions ts/docs/commands/permission/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ Changing only `keys`, `threshold` or `name` needs no such deletion.
| `--dry-run` | Mock receipt — fee, resulting-structure card, and warnings — matching a real submission; no signature, no broadcast, no password. Excludes `--sign-only` / `--build-only` |
| `--sign-only` | Build and sign, output the signed hex without broadcasting (feed [`tx broadcast`](../tx/broadcast.md) for on-chain co-signing). Excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex (feed [`tx multisig --create`](../tx/multisig.md) for service-relayed multi-sig). Excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only` |
| `--permission-id <n>` | Permission group to sign with — changing permissions is owner-level, so normally `0` (default `0`) |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); changing permissions normally uses `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
2 changes: 1 addition & 1 deletion ts/docs/commands/reward/withdraw.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Moves your accumulated voting rewards (plus block rewards if you are an SR) into
| `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 29=active); default `0` |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
2 changes: 1 addition & 1 deletion ts/docs/commands/stake/cancel-unfreeze.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Cancels **every** unstake still in its waiting period and rolls those amounts ba
| `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 29=active); default `0` |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
2 changes: 1 addition & 1 deletion ts/docs/commands/stake/delegate.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Check how much you can still delegate with [`stake delegated`](delegated.md) (`M
| `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 29=active); default `0` |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
2 changes: 1 addition & 1 deletion ts/docs/commands/stake/freeze.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Amount is in SUN (1 TRX = 1,000,000 SUN). Staked TRX stays yours; to get it back
| `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 29=active); default `0` |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
2 changes: 1 addition & 1 deletion ts/docs/commands/stake/undelegate.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Reclaiming is immediate (no waiting period — the TRX was staked all along, onl
| `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 29=active); default `0` |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
2 changes: 1 addition & 1 deletion ts/docs/commands/stake/unfreeze.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Stake 2.0 allows at most **32 pending unstakes** per account at a time; check re
| `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` |
| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` |
| `--expiration <ms>` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 29=active); default `0` |
| `--permission-id <n>` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` |
| `--wait` / `--wait-timeout <ms>` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) |
| `--password-stdin` | Master password from stdin |

Expand Down
Loading