Compare commits

..

15 Commits

Author SHA1 Message Date
cf440ace0d Merge pull request 'CI: conditional Flutter install + concurrency + wget→curl for Mac runner' (#15) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet Android build / build (push) Failing after 2m41s
Hash Wallet Linux build / build (push) Successful in 9m42s
Hash Wallet iOS Simulator build / build (push) Failing after 51m37s
Reviewed-on: #15
2026-05-18 21:31:07 -04:00
f1d6f165ea Merge pull request 'dev' (#14) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet iOS Simulator build / build (push) Failing after 3s
Hash Wallet Android build / build (push) Has been cancelled
Hash Wallet Linux build / build (push) Has been cancelled
Reviewed-on: #14
2026-05-18 21:26:28 -04:00
771938e545 Merge pull request 'dev' (#13) from github-such-software/hash-wallet:dev into dev
All checks were successful
Hash Wallet Linux build / build (push) Successful in 9m50s
Reviewed-on: #13
2026-05-17 20:36:07 -04:00
2867836992 Merge pull request 'Fix orphan references after shitcoin nuke (build round 2)' (#12) from github-such-software/hash-wallet:dev into dev
All checks were successful
Hash Wallet Linux build / build (push) Successful in 9m46s
Reviewed-on: #12
2026-05-17 19:21:19 -04:00
d7cb4f49b6 Merge pull request 'Nuke Solana/Tron/Zano/Decred/Zcash/Haven from the codebase entirely' (#11) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet Linux build / build (push) Failing after 8m46s
Reviewed-on: #11
2026-05-17 12:34:54 -04:00
834c0bd77e Merge pull request 'dev' (#10) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet Linux build / build (push) Failing after 8m36s
Reviewed-on: #10
2026-05-17 11:09:18 -04:00
9aea799166 Merge pull request 'dev' (#9) from github-such-software/hash-wallet:dev into dev
All checks were successful
Hash Wallet Linux build / build (push) Successful in 9m56s
Reviewed-on: #9
2026-05-16 18:40:17 -04:00
fa149ddfe8 Merge pull request 'Fix dangling 'if (!isMoneroOnly) ...[' in support_view_model.dart' (#8) from github-such-software/hash-wallet:dev into dev
All checks were successful
Hash Wallet Linux build / build (push) Successful in 10m0s
Reviewed-on: #8
2026-05-16 12:26:16 -04:00
63534084cb Merge pull request 'dev' (#7) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet Linux build / build (push) Has been cancelled
Reviewed-on: #7
2026-05-16 12:11:32 -04:00
e7ff15329d Merge pull request 'theme_list: sync to renamed BlackTheme accent enum entries' (#6) from github-such-software/hash-wallet:dev into dev
All checks were successful
Hash Wallet Linux build / build (push) Successful in 9m53s
Reviewed-on: #6
2026-05-16 10:52:02 -04:00
9236408bf1 Merge pull request 'dev' (#5) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet Linux build / build (push) Has been cancelled
Reviewed-on: #5
2026-05-16 10:37:44 -04:00
ebd46d957c Merge pull request 'CI: pin upload-artifact to v3 for Gitea Actions compatibility' (#4) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet Linux build / build (push) Failing after 15m25s
Reviewed-on: #4
2026-05-15 20:07:32 -04:00
dc5631c22a Merge pull request 'CI: fix secrets generator early-return bug + inject Trocador via sed' (#3) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet Linux build / build (push) Has been cancelled
Reviewed-on: #3
2026-05-15 19:52:12 -04:00
ad741b80a0 Merge pull request 'CI: serialize model_generator.sh + explicit Flutter precache step' (#2) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet Linux build / build (push) Has been cancelled
Reviewed-on: #2
2026-05-15 19:35:21 -04:00
136c7f32ef Merge pull request 'Remove Cake-specific CI workflows (broken on our setup)' (#1) from github-such-software/hash-wallet:dev into dev
Some checks failed
Hash Wallet Linux build / build (push) Failing after 6m24s
Reviewed-on: #1
2026-05-15 13:31:14 -04:00
303 changed files with 1460 additions and 2253 deletions

View File

@@ -1,13 +1,14 @@
name: Hash Bags Android build
name: Hash Wallet Android build
# Triggers:
# - push to dev/main (validate every commit)
# - PRs targeting dev/main (gate merges)
# - manual via workflow_dispatch
on:
# Manual-only for now. Trigger via Actions → "Hash Bags Android build"
# → Run workflow when you want a build. workflow_dispatch runs as the
# triggering user, so secrets are always available (unlike PR triggers).
push:
branches: [dev, main]
pull_request:
branches: [dev, main]
workflow_dispatch:
concurrency:
@@ -42,7 +43,7 @@ jobs:
run: |
git config --global --add safe.directory '*'
git config --global user.email "ci@suchsoftware.com"
git config --global user.name "Hash Bags CI"
git config --global user.name "Hash Wallet CI"
# ---- External prebuilt deps (same approach as Linux workflow) --------
- name: Fetch prebuilt torch_dart
@@ -100,50 +101,26 @@ jobs:
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
BUNDLE_DIR="scripts/monero_c/release/$MONERO_C_TAG"
# monero_c target dir → Android ABI dir. i686 (32-bit x86) is
# essentially dead on real Android devices; the monero_c bundle
# doesn't ship it. Missing target = log + skip, not fatal. The
# required modern ABIs (arm64-v8a, x86_64) MUST exist or we fail.
# monero_c target dir → Android ABI dir
declare -A ABI_MAP=(
[aarch64-linux-android]=arm64-v8a
[armv7a-linux-androideabi]=armeabi-v7a
[i686-linux-android]=x86
[x86_64-linux-android]=x86_64
)
REQUIRED_ABIS=(arm64-v8a x86_64)
declare -A FOUND_ABI
for target in "${!ABI_MAP[@]}"; do
abi="${ABI_MAP[$target]}"
if [[ ! -d "$BUNDLE_DIR/$target" ]]; then
echo "SKIP: $target not in bundle (Android ABI $abi will not be shipped)"
continue
fi
mkdir -p "android/app/src/main/jniLibs/$abi"
ok=true
for coin in monero wownero; do
src="$BUNDLE_DIR/$target/lib${coin}_wallet2_api_c.so"
if [[ -f "$src" ]]; then
cp -v "$src" "android/app/src/main/jniLibs/$abi/"
else
echo "SKIP: $src missing (skipping $abi)"
ok=false
break
echo "MISSING: $src — fail loud, the bundle layout may have changed"
exit 1
fi
done
if $ok; then
FOUND_ABI[$abi]=1
else
rm -rf "android/app/src/main/jniLibs/$abi"
fi
done
# Fail loudly only if a required ABI is missing.
for required in "${REQUIRED_ABIS[@]}"; do
if [[ -z "${FOUND_ABI[$required]:-}" ]]; then
echo "FATAL: required ABI $required is missing — monero_c bundle layout has changed"
exit 1
fi
done
echo "=== jniLibs contents ==="
@@ -188,19 +165,8 @@ jobs:
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
set -e
# Debug visibility — prints lengths only, never values. If a secret
# length is 0, Gitea Actions is not passing it to this run (most
# commonly because the run was triggered by a from-fork PR).
echo "ANDROID_KEYSTORE_BASE64 length: ${#ANDROID_KEYSTORE_BASE64}"
echo "ANDROID_KEYSTORE_PASSWORD length: ${#ANDROID_KEYSTORE_PASSWORD}"
echo "ANDROID_KEY_ALIAS length: ${#ANDROID_KEY_ALIAS}"
echo "ANDROID_KEY_PASSWORD length: ${#ANDROID_KEY_PASSWORD}"
if [[ -z "$ANDROID_KEYSTORE_BASE64" ]]; then
echo "FATAL: ANDROID_KEYSTORE_BASE64 not reaching the runner."
echo "Check: (1) secret is in repo Settings → Actions → Secrets;"
echo " (2) workflow was triggered by 'push' or 'workflow_dispatch'"
echo " (PR triggers from a fork strip secrets);"
echo " (3) Gitea's runner is configured to pass secrets."
echo "FATAL: ANDROID_KEYSTORE_BASE64 not set — configure Gitea Actions secrets first"
exit 1
fi
# Write decoded keystore next to build.gradle (storeFile path
@@ -228,20 +194,15 @@ jobs:
- name: Generate localization
run: dart run tool/generate_localization.dart
- name: Compile SVG assets (res/pictures/*.svg → assets/new-ui/*.svg.vec)
run: ./compile_graphics.sh
# ---- Compile + sign --------------------------------------------------
# Universal APK (single binary covers all 4 ABIs, easy to sideload).
- name: Build release APK (universal, all ABIs)
run: flutter build apk --dart-define-from-file=env.json --release
# AAB for Play Console upload (Google generates per-ABI APKs server-side
# via Play App Signing). --verbose surfaces the full gradle stacktrace
# when FinalizeBundleTask / signReleaseBundle fails — without it the
# error is opaque ('FinalizeBundleTask$BundleToolRunnable failed').
# via Play App Signing).
- name: Build release AAB
run: flutter build appbundle --dart-define-from-file=env.json --release --verbose
run: flutter build appbundle --dart-define-from-file=env.json --release
- name: Sanity-check signature on APK
run: |

View File

@@ -1,4 +1,4 @@
name: Hash Bags iOS Simulator build
name: Hash Wallet iOS Simulator build
# Phase 1 of iOS CI: validate the Mac runner end-to-end by producing an
# unsigned simulator .app. Drop the resulting Runner.app into the iOS
@@ -8,8 +8,10 @@ name: Hash Bags iOS Simulator build
# Phase 2 (separate workflow): full TestFlight pipeline with signing.
on:
# Manual-only for now. Trigger via Actions → "Hash Bags iOS Simulator
# build" → Run workflow when you want a build.
push:
branches: [dev, main]
pull_request:
branches: [dev, main]
workflow_dispatch:
# Cancel in-flight runs when a newer commit lands on the same branch — so a
@@ -28,16 +30,6 @@ jobs:
env:
APP_IOS_TYPE: cakewallet
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
# CocoaPods refuses to operate when LANG is ASCII-8BIT (default for the
# Gitea runner shell). Force UTF-8 globally so `pod install` succeeds.
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
# If you added an /etc/hosts entry on the Mac mini that points
# git.such.software at NPM's LAN IP (so the runner hits NPM's
# Let's Encrypt cert instead of Gitea's internal self-signed),
# this env var can be removed. Left in as a belt-and-suspenders
# fallback — has no effect if certs already verify.
NODE_TLS_REJECT_UNAUTHORIZED: '0'
steps:
- uses: actions/checkout@v4
@@ -66,60 +58,11 @@ jobs:
- name: Install CocoaPods (if missing)
run: |
if command -v pod >/dev/null; then
pod --version
exit 0
fi
if command -v brew >/dev/null; then
brew install cocoapods
else
export GEM_HOME="$HOME/.gem"
export PATH="$GEM_HOME/bin:$PATH"
echo "GEM_HOME=$HOME/.gem" >> "$GITHUB_ENV"
echo "$HOME/.gem/bin" >> "$GITHUB_PATH"
gem install --user-install cocoapods --no-document
if ! command -v pod >/dev/null; then
sudo gem install cocoapods --no-document
fi
pod --version
- name: Install Rust + iOS targets (if missing)
run: |
if ! command -v rustup >/dev/null; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --no-modify-path
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
export PATH="$HOME/.cargo/bin:$PATH"
fi
# Clean any partial downloads from a previously crashed install — those
# are the source of the 'could not rename .partial → final: No such
# file or directory' race that breaks parallel pod builds.
rm -rf "$HOME/.rustup/downloads"/*.partial 2>/dev/null || true
# Pre-install iOS targets serially so the pod build doesn't try to
# do it under concurrency.
rustup target add aarch64-apple-ios
rustup target add aarch64-apple-ios-sim
rustup target add x86_64-apple-ios
rustup show
- name: Install Go + gomobile (if missing)
run: |
if ! command -v go >/dev/null; then
if command -v brew >/dev/null; then
brew install go
else
echo "Go missing and brew not available"; exit 1
fi
fi
go version
# Ensure $(go env GOPATH)/bin is on PATH for subsequent steps.
GOPATH=$(go env GOPATH)
echo "$GOPATH/bin" >> "$GITHUB_PATH"
export PATH="$PATH:$GOPATH/bin"
# gomobile + gobind are needed by scripts/build_bitbox_flutter.sh
if ! command -v gomobile >/dev/null; then
go install golang.org/x/mobile/cmd/gomobile@latest
go install golang.org/x/mobile/cmd/gobind@latest
fi
which gomobile && gomobile version || true
- name: Show toolchain
run: |
set -x
@@ -153,23 +96,11 @@ jobs:
rm reown_flutter.tar.gz
popd
- name: Clone BitBox Flutter (iOS — skip Android bindings)
- name: Clone BitBox Flutter
run: |
# Pubspec has bitbox_flutter as a path: dep at scripts/bitbox_flutter,
# so the directory must exist for pub get. The bundled build_bindings.sh
# runs `gomobile bind -target=android` which needs the Android SDK we
# don't have on the Mac runner — and the resulting .aar is Android-only.
# iOS uses bitbox_flutter's native ios/Classes plugin, no .aar required.
set -x -e
pushd scripts
if [[ ! -d bitbox_flutter ]]; then
git clone https://github.com/konstantinullrich/bitbox_flutter
fi
cd bitbox_flutter
git fetch -a
git reset --hard
git checkout 5a6e6dd388ef64003f86094af80d5453518b601d
git reset --hard
./build_bitbox_flutter.sh
popd
# ---- Native crypto cores (monero_c prebuilt bundle) ------------------
@@ -187,27 +118,17 @@ jobs:
ls
popd
# iOS native lib staging: Cake's scripts/ios/setup.sh + gen_framework.sh
# expect pre-built .a archives in specific paths. The first run will
# likely surface what's missing; we iterate from there.
- name: Inspect iOS targets available in monero_c bundle
run: |
set -x
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
BUNDLE_DIR="scripts/monero_c/release/$MONERO_C_TAG"
echo "=== bundle top-level ==="
# List anything iOS-y
find "$BUNDLE_DIR" -maxdepth 1 -name '*ios*' -o -name '*apple*' 2>/dev/null || true
ls "$BUNDLE_DIR" || true
echo "=== iOS-y subdirs ==="
find "$BUNDLE_DIR" -maxdepth 1 -type d \( -name '*ios*' -o -name '*apple*' \) 2>/dev/null
echo "=== sample contents of one apple-ios target ==="
for d in "$BUNDLE_DIR"/aarch64-apple-ios "$BUNDLE_DIR"/aarch64-apple-ios-simulator; do
[[ -d "$d" ]] && { echo "--- $d ---"; ls "$d"; }
done
- name: Build MoneroWallet + WowneroWallet XCFrameworks
run: |
set -x -e
pushd scripts/ios
bash ./gen_framework.sh
popd
ls -la ios/MoneroWallet.xcframework ios/WowneroWallet.xcframework
# ---- Configure: pubspec.yaml, Info.plist, GeneratedPluginRegistrant ---
- name: Run iOS configure (cakewallet profile)
@@ -249,9 +170,6 @@ jobs:
- name: Generate localization
run: dart run tool/generate_localization.dart
- name: Compile SVG assets (res/pictures/*.svg → assets/new-ui/*.svg.vec)
run: ./compile_graphics.sh
# CocoaPods: the iOS Podfile installs Flutter plugin pods. Required
# before flutter build can link them.
- name: pod install
@@ -272,58 +190,6 @@ jobs:
cd build/ios/iphonesimulator
zip -r hash_wallet_ios_sim_${{ github.sha }}.zip Runner.app
# Confirm git.such.software resolves to a local target (the Mac itself).
# The runner inherits the Mac's /etc/hosts directly (host-mode act_runner),
# so the user should have:
# 127.0.0.1 git.such.software
# already in /etc/hosts. We only assert that here, don't modify (sudo
# without NOPASSWD prompts for a password the runner can't enter).
#
# macOS's `getent` doesn't exist and `nslookup` bypasses /etc/hosts.
# We grep /etc/hosts directly — that's what Node's getaddrinfo will
# actually consult.
- name: Verify Gitea hostname resolves locally
run: |
if grep -qE '^[[:space:]]*(127\.0\.0\.1|host\.docker\.internal|192\.168\.)[[:space:]]+git\.such\.software' /etc/hosts; then
echo "✓ /etc/hosts points git.such.software at a local target:"
grep git.such.software /etc/hosts
else
echo "✗ /etc/hosts is MISSING a local override for git.such.software."
echo " Node will resolve to the public IP and fail on NAT hairpin."
echo " Add (once, on the Mac mini):"
echo " sudo sh -c 'echo \"127.0.0.1 git.such.software\" >> /etc/hosts'"
exit 1
fi
# Diagnostic: print exactly where upload-artifact will try to send data.
# act_runner injects these env vars; their values tell us whether the
# upload targets git.such.software (uses our /etc/hosts override) or
# a LAN IP directly (override irrelevant). Also probe the URL with
# curl -k so we see the cert + reachability before the real upload.
- name: Diagnose Actions API endpoint
run: |
set +e
echo "=== Actions runtime env ==="
echo "ACTIONS_RUNTIME_URL=${ACTIONS_RUNTIME_URL:-<unset>}"
echo "ACTIONS_RESULTS_URL=${ACTIONS_RESULTS_URL:-<unset>}"
echo "ACTIONS_CACHE_URL=${ACTIONS_CACHE_URL:-<unset>}"
echo "GITHUB_SERVER_URL=${GITHUB_SERVER_URL:-<unset>}"
echo "GITHUB_API_URL=${GITHUB_API_URL:-<unset>}"
echo "NODE_TLS_REJECT_UNAUTHORIZED=${NODE_TLS_REJECT_UNAUTHORIZED:-<unset>}"
echo
# Probe each non-empty URL to see what Node will actually hit.
for var in ACTIONS_RUNTIME_URL ACTIONS_RESULTS_URL GITHUB_SERVER_URL; do
url="${!var}"
[ -z "$url" ] && continue
host=$(echo "$url" | sed -E 's|^https?://([^/:]+).*|\1|')
echo "--- $var → $url ---"
echo "DNS for $host:"
dscacheutil -q host -a name "$host" 2>/dev/null || nslookup "$host" 2>/dev/null || true
echo "HEAD probe (curl -kIv, 10s timeout):"
curl -kIv --max-time 10 "$url" 2>&1 | head -40 || true
echo
done
- name: Upload .app artifact
uses: actions/upload-artifact@v3
with:

View File

@@ -1,4 +1,4 @@
name: Hash Bags iOS TestFlight build
name: Hash Wallet iOS TestFlight build
# Phase 2 of iOS CI: full signed build that uploads to TestFlight.
# Phase 1 (build-ios-sim.yml) validates the Mac runner with no signing —
@@ -35,12 +35,6 @@ jobs:
env:
APP_IOS_TYPE: cakewallet
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
# CocoaPods needs UTF-8; Gitea runner shell defaults to ASCII-8BIT.
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
# See note in build-ios-sim.yml — fallback if /etc/hosts trick
# isn't in place. Safe no-op once it is.
NODE_TLS_REJECT_UNAUTHORIZED: '0'
steps:
- uses: actions/checkout@v4
@@ -67,63 +61,11 @@ jobs:
- name: Install CocoaPods (if missing)
run: |
if command -v pod >/dev/null; then
pod --version
exit 0
fi
if command -v brew >/dev/null; then
brew install cocoapods
else
export GEM_HOME="$HOME/.gem"
export PATH="$GEM_HOME/bin:$PATH"
echo "GEM_HOME=$HOME/.gem" >> "$GITHUB_ENV"
echo "$HOME/.gem/bin" >> "$GITHUB_PATH"
gem install --user-install cocoapods --no-document
if ! command -v pod >/dev/null; then
sudo gem install cocoapods --no-document
fi
pod --version
- name: Install Rust + iOS targets (if missing)
run: |
if ! command -v rustup >/dev/null; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --no-modify-path
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
export PATH="$HOME/.cargo/bin:$PATH"
fi
rm -rf "$HOME/.rustup/downloads"/*.partial 2>/dev/null || true
# Stable + iOS targets (most plugins use these)
rustup target add aarch64-apple-ios
rustup target add aarch64-apple-ios-sim
rustup target add x86_64-apple-ios
# Nightly + iOS targets — at least one plugin (sp_scanner / payjoin
# / something in the cargokit chain) builds its rust component
# with +nightly. The TestFlight archive bombs at "Missing manifest
# in toolchain 'nightly-aarch64-apple-darwin'" if nightly isn't
# installed alongside stable.
rustup toolchain install nightly --profile minimal
rustup target add aarch64-apple-ios --toolchain nightly
rustup target add aarch64-apple-ios-sim --toolchain nightly
rustup target add x86_64-apple-ios --toolchain nightly
rustup show
- name: Install Go + gomobile (if missing)
run: |
if ! command -v go >/dev/null; then
if command -v brew >/dev/null; then
brew install go
else
echo "Go missing and brew not available"; exit 1
fi
fi
go version
GOPATH=$(go env GOPATH)
echo "$GOPATH/bin" >> "$GITHUB_PATH"
export PATH="$PATH:$GOPATH/bin"
if ! command -v gomobile >/dev/null; then
go install golang.org/x/mobile/cmd/gomobile@latest
go install golang.org/x/mobile/cmd/gobind@latest
fi
which gomobile && gomobile version || true
- name: Show toolchain
run: |
set -x
@@ -144,25 +86,6 @@ jobs:
tar -xzf torch_dart.tar.gz -C torch_dart
rm torch_dart.tar.gz
popd
# LibTorch.xcframework ships with a lie: each slice's Info.plist
# claims MinimumOSVersion=12.0, but the Mach-O LC_BUILD_VERSION
# in the binaries says 13.0 (device) and 14.0 (simulator).
# Apple's TestFlight validator catches this mismatch and rejects
# the upload with ITMS-90208. Patch the Info.plists to match
# the actual binary build versions.
DEVICE_PLIST=scripts/torch_dart/ios/LibTorch.xcframework/ios-arm64/LibTorch.framework/Info.plist
SIM_PLIST=scripts/torch_dart/ios/LibTorch.xcframework/ios-arm64-simulator/LibTorch.framework/Info.plist
for entry in "$DEVICE_PLIST:13.0" "$SIM_PLIST:14.0"; do
plist="${entry%:*}"
min="${entry#*:}"
if [[ -f "$plist" ]]; then
plutil -replace MinimumOSVersion -string "$min" "$plist"
echo "patched $plist -> MinimumOSVersion $min"
plutil -p "$plist" | grep -E "MinimumOSVersion|CFBundleName" || true
else
echo "WARNING: $plist not found"
fi
done
- name: Fetch prebuilt reown_flutter
run: |
@@ -175,20 +98,11 @@ jobs:
rm reown_flutter.tar.gz
popd
- name: Clone BitBox Flutter (iOS — skip Android bindings)
- name: Clone BitBox Flutter
run: |
# See note in build-ios-sim.yml — iOS uses bitbox's native plugin,
# not the .aar that build_bindings.sh generates.
set -x -e
pushd scripts
if [[ ! -d bitbox_flutter ]]; then
git clone https://github.com/konstantinullrich/bitbox_flutter
fi
cd bitbox_flutter
git fetch -a
git reset --hard
git checkout 5a6e6dd388ef64003f86094af80d5453518b601d
git reset --hard
./build_bitbox_flutter.sh
popd
- name: Fetch prebuilt monero_c bundle
@@ -203,14 +117,6 @@ jobs:
rm release-bundle.zip
popd
- name: Build MoneroWallet + WowneroWallet XCFrameworks
run: |
set -x -e
pushd scripts/ios
bash ./gen_framework.sh
popd
ls -la ios/MoneroWallet.xcframework ios/WowneroWallet.xcframework
# ---- Configure: pubspec.yaml, Info.plist, etc. ----------------------
- name: Run iOS configure (cakewallet profile)
run: |
@@ -237,40 +143,6 @@ jobs:
lib/.secrets.g.dart
# ---- Apple signing setup --------------------------------------------
- name: Sanity-check Apple secrets are reaching the runner
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
APPLE_API_KEY_P8_BASE64: ${{ secrets.APPLE_API_KEY_P8_BASE64 }}
APPLE_DIST_CERT_P12_BASE64: ${{ secrets.APPLE_DIST_CERT_P12_BASE64 }}
APPLE_DIST_CERT_P12_PASSWORD: ${{ secrets.APPLE_DIST_CERT_P12_PASSWORD }}
APPLE_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }}
APPLE_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
run: |
# Print LENGTHS only — never the secret values. Length 0 means the
# secret isn't actually reaching the runner (most often: secret is
# set on a different Gitea repo than this workflow is running in).
set +e
echo "APPLE_TEAM_ID length: ${#APPLE_TEAM_ID}"
echo "APPLE_KEY_ID length: ${#APPLE_KEY_ID}"
echo "APPLE_ISSUER_ID length: ${#APPLE_ISSUER_ID}"
echo "APPLE_API_KEY_P8_BASE64 length: ${#APPLE_API_KEY_P8_BASE64}"
echo "APPLE_DIST_CERT_P12_BASE64 length: ${#APPLE_DIST_CERT_P12_BASE64}"
echo "APPLE_DIST_CERT_P12_PASSWORD length: ${#APPLE_DIST_CERT_P12_PASSWORD}"
echo "APPLE_PROVISIONING_PROFILE_BASE64 length: ${#APPLE_PROVISIONING_PROFILE_BASE64}"
echo "APPLE_KEYCHAIN_PASSWORD length: ${#APPLE_KEYCHAIN_PASSWORD}"
set -e
# Fail fast on any zero-length secret so we don't waste time on the
# subsequent steps.
for n in TEAM_ID KEY_ID ISSUER_ID API_KEY_P8_BASE64 DIST_CERT_P12_BASE64 DIST_CERT_P12_PASSWORD PROVISIONING_PROFILE_BASE64 KEYCHAIN_PASSWORD; do
var="APPLE_$n"
if [[ -z "${!var}" ]]; then
echo "FATAL: $var is empty — set it as a Gitea repo secret on the repo this workflow runs in (Builds/hash-wallet)."
exit 1
fi
done
- name: Create temp keychain + import distribution cert
env:
APPLE_DIST_CERT_P12_BASE64: ${{ secrets.APPLE_DIST_CERT_P12_BASE64 }}
@@ -285,22 +157,9 @@ jobs:
security unlock-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
# Add to user search list so codesign can find it
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | sed 's/"//g')
# Decode the user-uploaded .p12 directly. macOS Keychain Access
# exports .p12 with legacy PKCS12 crypto (RC2-40-CBC, PBE-SHA1)
# which is exactly what `security import` expects — no openssl
# conversion needed. Earlier attempts to "convert to legacy"
# via openssl 3.x failed because openssl 3.x has RC2-40-CBC
# disabled by default; that was a non-problem we made for
# ourselves. The original "Unknown format" failure was caused
# by a truncated base64 secret value, since fixed by re-pasting
# the secret via Gitea's API.
# Import the .p12
P12="$RUNNER_TEMP/dist.p12"
echo "$APPLE_DIST_CERT_P12_BASE64" | openssl base64 -d -A > "$P12"
echo "=== decoded .p12 ==="
ls -la "$P12"
xxd "$P12" | head -1
file "$P12" || true
echo "$APPLE_DIST_CERT_P12_BASE64" | base64 -d > "$P12"
security import "$P12" -k "$KEYCHAIN_PATH" -P "$APPLE_DIST_CERT_P12_PASSWORD" \
-T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
@@ -316,7 +175,7 @@ jobs:
PROFILES_DIR="$HOME/Library/MobileDevice/Provisioning Profiles"
mkdir -p "$PROFILES_DIR"
PROFILE="$RUNNER_TEMP/Hash_Wallet.mobileprovision"
echo "$APPLE_PROVISIONING_PROFILE_BASE64" | openssl base64 -d -A > "$PROFILE"
echo "$APPLE_PROVISIONING_PROFILE_BASE64" | base64 -d > "$PROFILE"
# Parse the .mobileprovision to get its UUID + Name (CMS-decoded plist).
PROFILE_UUID=$(security cms -D -i "$PROFILE" | plutil -extract UUID raw -o - -)
@@ -337,29 +196,9 @@ jobs:
run: |
set -e
mkdir -p "$HOME/.appstoreconnect/private_keys"
P8="$HOME/.appstoreconnect/private_keys/AuthKey_${APPLE_KEY_ID}.p8"
echo "$APPLE_API_KEY_P8_BASE64" | openssl base64 -d -A > "$P8"
echo "$APPLE_API_KEY_P8_BASE64" | base64 -d > "$HOME/.appstoreconnect/private_keys/AuthKey_${APPLE_KEY_ID}.p8"
ls -la "$HOME/.appstoreconnect/private_keys/"
# Diagnostics — verify the PEM file is well-formed without
# printing the private key body. A valid AuthKey .p8 must:
# - Start with "-----BEGIN PRIVATE KEY-----"
# - End with "-----END PRIVATE KEY-----"
# - openssl pkey -in file -noout must succeed (real ASN.1 parse)
echo "=== .p8 first/last lines (no key body) ==="
head -1 "$P8"
tail -1 "$P8"
echo "=== openssl parse check (silent on success) ==="
if openssl pkey -in "$P8" -noout 2>&1; then
echo "OK — .p8 parses as a valid EC private key"
else
echo "FATAL: .p8 doesn't parse — secret likely truncated or wrong content"
echo "Re-paste APPLE_API_KEY_P8_BASE64 via the Gitea API (same flow"
echo "as the .p12 re-paste); the file decoded to:"
wc -c "$P8"
exit 1
fi
# ---- Flutter init + codegen + pod install ---------------------------
- name: Initialize Flutter SDK (iOS precache)
run: |
@@ -372,44 +211,11 @@ jobs:
- name: Generate localization
run: dart run tool/generate_localization.dart
- name: Compile SVG assets (res/pictures/*.svg → assets/new-ui/*.svg.vec)
run: ./compile_graphics.sh
- name: pod install
run: |
cd ios
pod install --repo-update
# ---- Override signing in project.pbxproj for the archive step -------
# The committed Release config uses Automatic signing + "Apple
# Development" identity (set up for local dev where Xcode is signed
# into an Apple ID). On CI we have only the cert + provisioning
# profile we installed in the temp keychain; no Xcode account.
#
# First attempt was an xcconfig append, but Xcode build-setting
# precedence puts target-level pbxproj settings ABOVE xcconfig —
# so the pbxproj values won and the build did Automatic signing
# anyway. Patch the Release config in pbxproj directly, only
# within the Runner target block (id 97C147071CF9000F007C117D);
# leaves Debug/Profile configs untouched so local builds stay
# on Automatic signing.
- name: Override signing settings in pbxproj for archive
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
set -x
PBX=ios/Runner.xcodeproj/project.pbxproj
# BSD sed (macOS): -i '' for in-place, regex range scoped to
# the Runner target's Release config by its stable UUID.
sed -i '' "/97C147071CF9000F007C117D \/\* Release/,/name = Release;/ {
s/CODE_SIGN_IDENTITY = \"Apple Development\";/CODE_SIGN_IDENTITY = \"Apple Distribution\";/
s/CODE_SIGN_STYLE = Automatic;/CODE_SIGN_STYLE = Manual;/
s/DEVELOPMENT_TEAM = [A-Z0-9]\{1,\};/DEVELOPMENT_TEAM = ${APPLE_TEAM_ID};/
s|PROVISIONING_PROFILE_SPECIFIER = \"\";|PROVISIONING_PROFILE_SPECIFIER = \"${PROFILE_NAME}\";|
}" "$PBX"
echo "=== patched Runner Release config ==="
sed -n '/97C147071CF9000F007C117D \/\* Release/,/name = Release;/p' "$PBX"
# ---- Generate ExportOptions.plist + build IPA -----------------------
- name: Write ExportOptions.plist
env:

View File

@@ -1,13 +1,14 @@
name: Hash Bags Linux build
name: Hash Wallet Linux build
# Triggers:
# - on push to dev/main (validates the foundation each time we land work)
# - on PRs targeting dev/main (gates merges)
# - manual via workflow_dispatch ("Run workflow" button in the UI)
on:
# Manual-only for now — auto-run on every push was too noisy. Trigger
# via Actions → "Hash Bags Linux build" → Run workflow when you want a
# build. Add `push:` back here if/when we want pre-merge validation.
push:
branches: [dev, main]
pull_request:
branches: [dev, main]
workflow_dispatch:
concurrency:
@@ -47,7 +48,7 @@ jobs:
run: |
git config --global --add safe.directory '*'
git config --global user.email "ci@suchsoftware.com"
git config --global user.name "Hash Bags CI"
git config --global user.name "Hash Wallet CI"
# ---- Secrets ---------------------------------------------------------
# IMPORTANT: don't pre-write any of the tool/.*secrets-config.json
@@ -161,93 +162,22 @@ jobs:
- name: Generate localization
run: dart run tool/generate_localization.dart
- name: Compile SVG assets (res/pictures/*.svg → assets/new-ui/*.svg.vec)
run: ./compile_graphics.sh
# ---- Compile + package ----------------------------------------------
- name: Build Linux app
run: flutter build linux --dart-define-from-file=env.json --release
# ---- Package as AppImage --------------------------------------------
# Self-contained .AppImage is the user-facing Linux deliverable: single
# file, double-click to run, no install, works on any glibc Linux from
# the last few years. appimagetool is run with --appimage-extract-and-run
# so it works inside the container without FUSE.
- name: Stage AppDir
- name: Compress release bundle
run: |
set -e -x
REL=build/linux/x64/release
APPDIR=$REL/Hash_Bags.AppDir
rm -rf "$APPDIR"
mkdir -p "$APPDIR/usr/bin" \
"$APPDIR/usr/share/applications" \
"$APPDIR/usr/share/icons/hicolor/256x256/apps"
# The whole Flutter Linux bundle (binary + data/ + lib/) lives
# under usr/bin/. The Flutter binary has RPATH=$ORIGIN/lib, so
# plugin/native libs in usr/bin/lib/ resolve naturally.
cp -r "$REL/bundle"/* "$APPDIR/usr/bin/"
# Icon: scale the iOS marketing icon to 256 if convert is around,
# else copy as-is (appimagetool only requires that AppDir root and
# the hicolor path contain a same-named PNG).
ICON_SRC=assets/images/ios_icons/hashwallet_ios_icons/Icon-App-1024x1024@1x.png
ICON_DEST="$APPDIR/usr/share/icons/hicolor/256x256/apps/hash_bags.png"
if command -v convert >/dev/null 2>&1; then
convert "$ICON_SRC" -resize 256x256 "$ICON_DEST"
else
cp "$ICON_SRC" "$ICON_DEST"
fi
cp "$ICON_DEST" "$APPDIR/hash_bags.png"
# .desktop file (at both canonical locations; appimagetool checks both)
cat > "$APPDIR/hash_bags.desktop" <<'DESKTOP'
[Desktop Entry]
Name=Hash Bags
Comment=Non-custodial multi-chain crypto wallet
Exec=hash_wallet
Icon=hash_bags
Type=Application
Categories=Office;Finance;
Terminal=false
DESKTOP
# Strip the leading whitespace from the heredoc
sed -i 's/^ //' "$APPDIR/hash_bags.desktop"
cp "$APPDIR/hash_bags.desktop" "$APPDIR/usr/share/applications/"
# AppRun: the entry point AppImage executes. Adds the bundled lib/
# dir to LD_LIBRARY_PATH and exec's the Flutter binary.
cat > "$APPDIR/AppRun" <<'APPRUN'
#!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export LD_LIBRARY_PATH="${HERE}/usr/bin/lib:${LD_LIBRARY_PATH}"
exec "${HERE}/usr/bin/hash_wallet" "$@"
APPRUN
sed -i 's/^ //' "$APPDIR/AppRun"
chmod +x "$APPDIR/AppRun"
ls -la "$APPDIR"
- name: Build AppImage
run: |
set -e -x
REL=build/linux/x64/release
wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /tmp/appimagetool
chmod +x /tmp/appimagetool
# --appimage-extract-and-run avoids the FUSE requirement (no fusermount
# in the build container). ARCH= tells appimagetool which arch tag to
# embed in the filename.
ARCH=x86_64 /tmp/appimagetool --appimage-extract-and-run \
"$REL/Hash_Bags.AppDir" \
"$REL/Hash_Bags-x86_64.AppImage"
ls -la "$REL"/*.AppImage
pushd build/linux/x64/release
zip -r hash_wallet_linux_${{ github.sha }}.zip bundle
popd
# actions/upload-artifact@v4 uses an HTTP API that Gitea Actions does
# not implement (only the v1/v3 wire format). Pinned to @v3 for Gitea
# compatibility — also works on GitHub Actions.
- name: Upload AppImage artifact
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: hash-wallet-linux-appimage-${{ github.sha }}
path: build/linux/x64/release/Hash_Bags-x86_64.AppImage
name: hash-wallet-linux-${{ github.sha }}
path: build/linux/x64/release/hash_wallet_linux_*.zip
retention-days: 14

View File

@@ -1,348 +0,0 @@
name: Hash Bags Windows build
# Phase 1 of Windows CI: produce an unsigned Windows release bundle (.zip of
# the Flutter Release output + MSVC runtime DLLs). Drop the zip on any Win
# 10/11 box, extract, run hash_wallet.exe.
#
# Phase 2 (separate iteration): wire up Inno Setup installer once the
# scripts/windows/build_exe_installer.iss file is rebranded from Cake Wallet
# → Hash Bags.
on:
# Manual-only. Trigger via Actions → "Hash Bags Windows build" → Run workflow.
# workflow_dispatch runs as the triggering user, so secrets are always
# available (PR triggers from forks would strip them — not relevant here
# since the runner is self-hosted and listening on a self-hosted Gitea).
workflow_dispatch:
# Cancel in-flight runs when a newer commit lands on the same branch.
concurrency:
group: windows-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
# Explicit Git Bash path (not `shell: bash`) because Windows resolves
# bare `bash` to C:\Windows\System32\bash.exe (WSL), which refuses to
# run when the act_runner service is hosted under LocalSystem:
# "Running WSL as local system is not supported."
# Git Bash works fine under LocalSystem and matches how GitHub's
# hosted windows-latest runner invokes bash.
shell: '"C:\Program Files\Git\bin\bash.exe" --noprofile --norc -eo pipefail {0}'
jobs:
build:
# Matches the label our act_runner.exe advertised: "windows:host" → executor=host.
runs-on: windows
env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
# Belt-and-suspenders: bypass Node TLS verification for actions/* that
# talk back to git.such.software. Same fallback as build-ios-sim.yml.
NODE_TLS_REJECT_UNAUTHORIZED: '0'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
# ---- Git config for LocalSystem ---------------------------------------
# The runner service runs as LocalSystem, which has no global git
# identity. scripts/monero_c/apply_patches.sh (and a few other
# submodule operations) do `git commit`-equivalents that need
# user.email + user.name set. Also pre-empt git's "safe.directory"
# check, which fires when the checkout dir owner != runner identity.
- name: Configure git identity + safe.directory
run: |
git config --global --add safe.directory '*'
git config --global user.email "ci@suchsoftware.com"
git config --global user.name "Hash Bags CI"
git config --global --list
# ---- Toolchain sanity ------------------------------------------------
# Our self-hosted runner has Flutter / Rust / VS Build Tools / Inno
# Setup pre-installed (see scripts/windows/setup-windows-runner.ps1).
# This step just prints versions so failures are easy to diagnose.
- name: Show installed toolchain versions
run: |
set -x
# PATH may need refreshing after install — refresh from the registry.
# Git Bash inherits Windows PATH at shell start, so just printing
# `which` is enough.
which flutter dart cargo rustc git 2>/dev/null || true
flutter --version
dart --version
rustc --version
cargo --version
# ---- External path-dep plugins ----------------------------------------
# Several plugins are path-deps in pubspec.yaml. The path directories
# must exist BEFORE `flutter pub get` runs against the regenerated
# pubspec, or pub fails with "could not find package X at <path>".
#
# The Android + iOS + Linux workflows use upstream-published .tar.gz
# tarballs. We don't, because those tarballs contain example/*
# symlinks pointing to absolute /home/runner/.pub-cache/ paths
# (artifacts from the upstream build host) — Linux tar tolerates
# dangling symlinks, Windows tar bails the whole extraction. Clone
# the source repos at the matching tagged versions instead. Same
# pattern as bitbox_flutter further down.
- name: Clone torch_dart at v1.0.17
run: |
set -x -e
pushd scripts
rm -rf torch_dart
git clone --depth 1 --branch v1.0.17 https://github.com/MrCyjaneK/torch_dart
popd
- name: Clone reown_flutter at v0.0.4
run: |
set -x -e
pushd scripts
rm -rf reown_flutter
git clone --depth 1 --branch v0.0.4 https://github.com/cake-tech/reown_flutter
popd
- name: Clone BitBox Flutter
run: |
# Pubspec has bitbox_flutter as a path dep — the directory must
# exist for pub get even if BitBox Windows support isn't
# functional. Same approach as the iOS workflow: clone the repo
# at a pinned commit, don't run any Android-binding build step.
set -x -e
pushd scripts
if [[ ! -d bitbox_flutter ]]; then
git clone https://github.com/konstantinullrich/bitbox_flutter
fi
cd bitbox_flutter
git fetch -a
git reset --hard
git checkout 5a6e6dd388ef64003f86094af80d5453518b601d
git reset --hard
popd
# ---- Native crypto cores (monero_c prebuilt bundle) ------------------
# The same release-bundle.zip that Android + iOS use. Contains
# pre-cross-compiled native libs for many target triples. We'll need
# the x86_64-w64-mingw32 (Windows MinGW) or x86_64-pc-windows-msvc
# entries — inspection step below logs what's actually shipped.
- name: Fetch prebuilt monero_c bundle
run: |
set -x -e
./scripts/prepare_moneroc.sh
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
echo "monero_c TAG: $MONERO_C_TAG"
mkdir -p "scripts/monero_c/release/$MONERO_C_TAG"
pushd "scripts/monero_c/release/$MONERO_C_TAG"
curl -fsSL -O https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
unzip -q release-bundle.zip
rm release-bundle.zip
echo "=== bundle contents (top level) ==="
ls
popd
- name: Inspect Windows targets in monero_c bundle
run: |
set -x
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
BUNDLE_DIR="scripts/monero_c/release/$MONERO_C_TAG"
echo "=== full mingw32 dir contents ==="
ls -la "$BUNDLE_DIR/x86_64-w64-mingw32" 2>/dev/null || true
# ---- Stage monero_c DLLs into the layout windows/CMakeLists.txt expects
# Upstream Cake's Windows build cross-compiles monero_c on Linux via
# scripts/windows/build_all.sh, which lays files out as:
# scripts/monero_c/release/<coin>/<triple>_<basename>.dll
# The CMakeLists.txt at windows/CMakeLists.txt:84-94 hardcodes those
# paths in install(FILES ...) rules — it copies + RENAMEs them to the
# final names (monero_libwallet2_api_c.dll etc.) in the Release dir.
#
# We're using the prebuilt bundle from MrCyjaneK's release, which has
# a different layout:
# scripts/monero_c/release/<TAG>/<triple>/lib<coin>_wallet2_api_c.dll
# So we restage the bundle's files into the upstream-build-all-style
# layout before flutter build windows runs.
- name: Stage monero_c bundle into upstream cross-compile layout
run: |
set -x -e
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
SRC="scripts/monero_c/release/$MONERO_C_TAG/x86_64-w64-mingw32"
MONERO_DST="scripts/monero_c/release/monero"
WOWNERO_DST="scripts/monero_c/release/wownero"
mkdir -p "$MONERO_DST" "$WOWNERO_DST"
# The wallet C-API DLLs, one per coin
cp -v "$SRC/libmonero_wallet2_api_c.dll" "$MONERO_DST/x86_64-w64-mingw32_libwallet2_api_c.dll"
cp -v "$SRC/libwownero_wallet2_api_c.dll" "$WOWNERO_DST/x86_64-w64-mingw32_libwallet2_api_c.dll"
# MinGW runtime DLLs (libssp-0, libwinpthread-1) — the wallet libs
# are dynamically linked against these. MrCyjaneK's prebuilt
# release-bundle.zip does NOT ship them (only the wallet *.dll +
# *.dll.a import libs), so we source them from Git Bash's bundled
# MinGW-w64 distribution, which ships the same x86_64-w64-mingw32
# ABI used to build the wallet libs.
# ORDERING MATTERS: monero_c's prebuilt wallet DLLs are built against
# recent MinGW-w64 (post-2022 winpthreads ABI with pthread_cond_timedwait64).
# Flutter's bundled mingit ships a 2017-vintage libwinpthread that's
# missing newer symbols and crashes wallet creation at runtime. Prefer
# Git for Windows' mingw64/bin which is updated frequently.
RUNTIME_PATHS=(
"/c/Program Files/Git/mingw64/bin" # Git for Windows — modern MinGW runtime (preferred)
"$SRC" # monero_c bundle (in case it grows DLLs)
"/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin" # choco install -y mingw
"/c/flutter/bin/mingit/mingw64/bin" # Flutter's bundled mingit — old (2017), use only as last resort
"/mingw64/bin" # MSYS2 fallback
)
for runtime in libssp-0.dll libwinpthread-1.dll; do
src=""
for d in "${RUNTIME_PATHS[@]}"; do
if [[ -f "$d/$runtime" ]]; then
src="$d/$runtime"
break
fi
done
if [[ -z "$src" ]]; then
echo "FATAL: $runtime not found in any of:"
printf ' %s\n' "${RUNTIME_PATHS[@]}"
for d in "${RUNTIME_PATHS[@]}"; do
echo "--- contents of $d (if exists) ---"
ls "$d" 2>/dev/null | head -20 || echo "(missing)"
done
exit 1
fi
cp -v "$src" "$MONERO_DST/x86_64-w64-mingw32_${runtime}"
done
echo "=== final staged layout ==="
ls -la "$MONERO_DST/" "$WOWNERO_DST/"
# ---- Configure: pubspec.yaml + per-coin enablement -------------------
# Mirror of hashwallet.bat — drives tool/configure.dart with the same
# set of coin flags that upstream Cake's Windows build uses.
- name: Configure pubspec + Wallet types
run: |
set -x -e
cp -f pubspec_description.yaml pubspec.yaml
flutter pub get
dart run tool/generate_pubspec.dart
flutter pub get
dart run tool/configure.dart \
--monero --bitcoin --ethereum --polygon --nano --bitcoinCash \
--wownero --dogecoin --base --arbitrum --bsc
# ---- Secrets ---------------------------------------------------------
- name: Generate per-module secrets.g.dart files (empty defaults)
run: dart run tool/generate_new_secrets.dart
- name: Inject Trocador affiliate secrets into lib/.secrets.g.dart
env:
TROCADOR_API_KEY: ${{ secrets.TROCADOR_API_KEY }}
TROCADOR_MONERO_API_KEY: ${{ secrets.TROCADOR_MONERO_API_KEY }}
TROCADOR_EXCHANGE_MARKUP: ${{ secrets.TROCADOR_EXCHANGE_MARKUP }}
run: |
# Length-only debug visibility — never prints actual secrets.
echo "TROCADOR_API_KEY length: ${#TROCADOR_API_KEY}"
echo "TROCADOR_MONERO_API_KEY length: ${#TROCADOR_MONERO_API_KEY}"
echo "TROCADOR_EXCHANGE_MARKUP length: ${#TROCADOR_EXCHANGE_MARKUP}"
if [[ -z "$TROCADOR_API_KEY" ]]; then
echo "WARN: TROCADOR_API_KEY not reaching runner — build will proceed"
echo " but Trocador exchange features won't work in this binary."
fi
sed -i \
-e "s|const trocadorApiKey = '';|const trocadorApiKey = '${TROCADOR_API_KEY}';|" \
-e "s|const trocadorMoneroApiKey = '';|const trocadorMoneroApiKey = '${TROCADOR_MONERO_API_KEY}';|" \
-e "s|const trocadorExchangeMarkup = '';|const trocadorExchangeMarkup = '${TROCADOR_EXCHANGE_MARKUP:-1}';|" \
lib/.secrets.g.dart
grep '^const trocador' lib/.secrets.g.dart
# ---- Codegen ---------------------------------------------------------
- name: Build generated code (mobx + hive adapters)
run: bash model_generator.sh
- name: Generate localization
run: dart run tool/generate_localization.dart
- name: Compile SVG assets (res/pictures/*.svg → assets/new-ui/*.svg.vec)
run: ./compile_graphics.sh
# ---- Build the Windows .exe ------------------------------------------
- name: Build Windows release
run: |
set -x -e
flutter config --enable-windows-desktop
flutter build windows --dart-define-from-file=env.json --release --verbose
# ---- Reconcile monero/wownero DLL naming for Dart FFI ----------------
# windows/CMakeLists.txt's install(FILES ... RENAME ...) rules produce
# <coin>_libwallet2_api_c.dll, but package:monero's Dart FFI loader
# (mrcyjanek's package) opens lib<coin>_wallet2_api_c.dll — different
# filename layout, runtime error "Failed to load dynamic library".
# Create the lib-prefixed copies so both name conventions resolve.
- name: Mirror monero/wownero DLLs under lib-prefixed names
run: |
set -x -e
DST="build/windows/x64/runner/Release"
for coin in monero wownero; do
src="$DST/${coin}_libwallet2_api_c.dll"
dst="$DST/lib${coin}_wallet2_api_c.dll"
if [[ -f "$src" ]]; then
cp -v "$src" "$dst"
else
echo "WARN: $src not in Release dir — CMake install may have skipped it"
ls "$DST" | head -20
fi
done
# ---- Bundle MSVC runtime DLLs with the .exe --------------------------
# Standalone Windows builds need msvcp140.dll + vcruntime140.dll +
# vcruntime140_1.dll next to the .exe (or installed via vc_redist on
# the user's machine). Bundling avoids the "VCRUNTIME140.dll not found"
# error on machines without VC++ redistributable installed.
- name: Copy MSVC runtime DLLs next to the .exe
run: |
set -x -e
# Find the redist dir for the currently-installed MSVC version (the
# version number rolls forward with VS updates, so do NOT hardcode).
REDIST_BASE="/c/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/VC/Redist/MSVC"
REDIST_DIR=$(find "$REDIST_BASE" -type d -name 'Microsoft.VC*.CRT' -path '*/x64/*' 2>/dev/null | sort | tail -1)
if [[ -z "$REDIST_DIR" ]]; then
echo "FATAL: could not find VC redist dir under $REDIST_BASE"
ls "$REDIST_BASE" 2>/dev/null || echo "(redist base dir missing entirely)"
exit 1
fi
echo "Using redist dir: $REDIST_DIR"
cp -v "$REDIST_DIR/msvcp140.dll" "$REDIST_DIR/vcruntime140.dll" "$REDIST_DIR/vcruntime140_1.dll" \
build/windows/x64/runner/Release/
# ---- Package + upload ------------------------------------------------
- name: List built artifacts (for debug visibility)
run: |
ls -la build/windows/x64/runner/Release/
# ---- Package + upload as zip -----------------------------------------
# The Flutter Windows build produces a directory tree (~500 files —
# HashWallet.exe + plugin DLLs + data/ + locales/ + bundled monero_c
# + MinGW runtime DLLs). Ship as a zip; users extract and run.
#
# Tried wrapping with 7-Zip SFX for a single-file "portable" .exe
# earlier but path_provider_windows computes the wallet storage dir
# from the .exe location, and SFX extracts to a different %TEMP%
# subdir on every launch — wallet keys couldn't be found across
# runs. Multi-file zip is what Firefox Portable / VS Code Portable
# / every other Flutter Windows desktop app does too.
- name: Create Hash Bags Windows release zip
run: |
set -x -e
cd build/windows/x64/runner/Release
# PowerShell's Compress-Archive is in PATH; no extra deps needed.
powershell -NoProfile -Command "Compress-Archive -Path .\* -DestinationPath ..\..\..\..\..\HashBags-windows-${{ github.sha }}.zip -Force"
cd "$GITHUB_WORKSPACE"
ls -la HashBags-windows-*.zip
echo "Size: $(du -h HashBags-windows-${{ github.sha }}.zip | cut -f1)"
- name: Upload Hash Bags Windows zip
uses: actions/upload-artifact@v3
with:
name: hash-bags-windows-${{ github.sha }}
path: HashBags-windows-${{ github.sha }}.zip
retention-days: 14

4
.gitignore vendored
View File

@@ -233,7 +233,3 @@ scripts/reown_flutter
# Hash Wallet: untracked notes / internal playbooks
docs/links.md
docs/UPSTREAM_SYNC.md
scripts/windows/setup-windows-runner.ps1
# Hash Wallet: local mirror of hash.boats website (deploy target on suchwow server)
hash.boats/

View File

@@ -1,10 +1,10 @@
# Contributing to Hash Bags
# Contributing to Hash Wallet
Thanks for the interest. Before opening a PR, please read this.
## Project shape
Hash Bags is a **friendly fork** of [Cake Wallet](https://github.com/cake-tech/cake_wallet). We periodically merge upstream changes and try to keep our divergence narrow. Every change you propose should be evaluated through that lens: will this make future upstream syncs harder, and if so, is the value worth the cost?
Hash Wallet is a **friendly fork** of [Cake Wallet](https://github.com/cake-tech/cake_wallet). We periodically merge upstream changes and try to keep our divergence narrow. Every change you propose should be evaluated through that lens: will this make future upstream syncs harder, and if so, is the value worth the cost?
## Branch model
@@ -33,20 +33,20 @@ dart run tool/configure.dart --monero --wownero --bitcoin --bitcoinCash \
--ethereum --polygon --base --arbitrum --bsc --nano --dogecoin
```
(That's the canonical Hash Bags build flag set. Don't pass `--solana`, `--tron`, `--zano`, `--decred`, or `--zcash` — those packages were deleted.)
(That's the canonical Hash Wallet build flag set. Don't pass `--solana`, `--tron`, `--zano`, `--decred`, or `--zcash` — those packages were deleted.)
## Code style
- Match the surrounding style. We have not introduced new lints beyond Cake's.
- New observable state goes through MobX; persisted state through Hive.
- Don't add new top-level analytics. Hash Bags ships with no telemetry by design.
- Don't add new top-level analytics. Hash Wallet ships with no telemetry by design.
- Don't import `package:cw_zano/`, `package:cw_tron/`, `package:cw_solana/`, `package:cw_decred/` — those packages are deleted. The corresponding `WalletType.*` enum values still exist in `cw_core/lib/wallet_type.dart` for backwards-compat but are never instantiated.
- Avoid adding `cake_*`-prefixed file or symbol names in new code. Use `hash_wallet`-prefixed where the symbol is ours, and leave Cake-prefixed names alone where they're inherited (so upstream merges still apply cleanly).
## What we generally won't accept
- Re-enabling Solana, Tron, Zano, Decred, or Zcash. The fork exists in part to drop these — opening that door defeats the purpose.
- New buy/sell provider integrations that require routing through Cake's `exchange-helper.cakewallet.com` proxy. Direct provider APIs are fine if we can register Hash Bags / Such Software LLC as the partner.
- New buy/sell provider integrations that require routing through Cake's `exchange-helper.cakewallet.com` proxy. Direct provider APIs are fine if we can register Hash Wallet / Such Software LLC as the partner.
- Lightning Network integration. Greenlight (and self-hosted alternatives) require running infrastructure we're not in a position to operate yet.
- Cake Pay re-enable. The infrastructure is Cake Labs's; we can't run it.
- New defaults that point at `cakewallet.com` hosts. We're moving away from depending on Cake's infrastructure — community nodes, our own proxies (`exchange-helper.such.software`, `prices.neroswap.com`), or both, are preferred.
@@ -58,7 +58,7 @@ dart run tool/configure.dart --monero --wownero --bitcoin --bitcoinCash \
- Build reproducibility improvements (especially Docker / CI parity).
- Privacy-leaning defaults (own-node guidance, Tor/I2P integration improvements, fewer outbound calls).
- Wallet-core bug fixes — these we will happily upstream to Cake.
- Documentation, especially around the build pipeline and the Cake → Hash Bags diff.
- Documentation, especially around the build pipeline and the Cake → Hash Wallet diff.
## Reporting bugs
@@ -67,7 +67,7 @@ Open an issue at https://github.com/Such-Software/hash-wallet/issues. Please inc
- Build artifact ID (the CI workflow names the artifact by commit SHA).
- Platform + OS version.
- Terminal output if you built and ran from a terminal — many error popups don't print to stderr; check `<appDir>/error.txt` (typically `~/.config/hash_wallet/error.txt` on Linux).
- Whether the bug also reproduces in the latest Cake Wallet build (so we know whether it's a Cake-upstream bug or a Hash Bags regression).
- Whether the bug also reproduces in the latest Cake Wallet build (so we know whether it's a Cake-upstream bug or a Hash Wallet regression).
## Security disclosures
@@ -75,4 +75,4 @@ See [`docs/SECURITY.md`](docs/SECURITY.md). Email `support@such.software` for an
## License
By contributing, you agree your contribution is licensed under the [MIT License](LICENSE.md), the same as the rest of Hash Bags.
By contributing, you agree your contribution is licensed under the [MIT License](LICENSE.md), the same as the rest of Hash Wallet.

View File

@@ -3,12 +3,12 @@ PRIVACY POLICY
Last Updated: May 16, 2026
Such Software LLC ("we", "us", or "our") respects your privacy. Hash Bags is engineered to be a strictly non-custodial software application. Our privacy philosophy is simple: we cannot leak or misuse data that we do not collect.
Such Software LLC ("we", "us", or "our") respects your privacy. Hash Wallet is engineered to be a strictly non-custodial software application. Our privacy philosophy is simple: we cannot leak or misuse data that we do not collect.
1. INFORMATION WE DO NOT COLLECT
--------------------------------
Hash Bags operates entirely on your device. We do not collect, transmit, store, or have access to:
Hash Wallet operates entirely on your device. We do not collect, transmit, store, or have access to:
- Your private keys, public keys, or mnemonic seed phrases.
- Your wallet passwords or PINs.
@@ -19,7 +19,7 @@ Hash Bags operates entirely on your device. We do not collect, transmit, store,
2. HOW THE SOFTWARE CONNECTS TO BLOCKCHAINS
-------------------------------------------
To fetch balances and broadcast transactions, Hash Bags must connect to remote blockchain nodes.
To fetch balances and broadcast transactions, Hash Wallet must connect to remote blockchain nodes.
By default, the Software may connect to nodes hosted by third parties or by us.
@@ -30,7 +30,7 @@ We highly recommend running your own node and configuring the Software to connec
3. THIRD-PARTY SERVICES (TROCADOR)
----------------------------------
Hash Bags integrates with a third-party swap aggregator, Trocador, to allow you to exchange digital assets natively within the interface.
Hash Wallet integrates with a third-party swap aggregator, Trocador, to allow you to exchange digital assets natively within the interface.
When you initiate a swap, the necessary transaction details (such as the sending address, receiving address, and amount) are transmitted directly to Trocador's API.
@@ -41,7 +41,7 @@ Such Software LLC does not intercept, log, or store the data you transmit to Tro
4. APP STORES AND CRASH REPORTS
-------------------------------
If you download Hash Bags through the Apple App Store or Google Play Store, those platforms may independently collect analytics or crash reports based on your device's operating system settings. We only view anonymized, aggregated metrics provided by these storefronts.
If you download Hash Wallet through the Apple App Store or Google Play Store, those platforms may independently collect analytics or crash reports based on your device's operating system settings. We only view anonymized, aggregated metrics provided by these storefronts.
5. CONTACT US
-------------

View File

@@ -1,10 +1,10 @@
# Hash Bags
# Hash Wallet
**Hash Bags** (`#`) is an open-source, non-custodial, multi-currency crypto wallet for Android, iOS, macOS, Linux, and Windows. It is a fork of [Cake Wallet](https://github.com/cake-tech/cake_wallet) maintained by [Such Software LLC](https://github.com/Such-Software).
**Hash Wallet** (`#`) is an open-source, non-custodial, multi-currency crypto wallet for Android, iOS, macOS, Linux, and Windows. It is a fork of [Cake Wallet](https://github.com/cake-tech/cake_wallet) maintained by [Such Software LLC](https://github.com/Such-Software).
## Why a fork?
Cake Wallet ended Wownero support in early 2026. Hash Bags exists to keep Wownero as a first-class mobile experience, run by the project's founder, while shipping a slimmer, more focused multi-coin wallet without the chains and integrations we don't want to maintain or recommend.
Cake Wallet ended Wownero support in early 2026. Hash Wallet exists to keep Wownero as a first-class mobile experience, run by the project's founder, while shipping a slimmer, more focused multi-coin wallet without the chains and integrations we don't want to maintain or recommend.
## Supported chains
@@ -75,11 +75,11 @@ Issues and PRs welcome at https://github.com/Such-Software/hash-wallet. See [`CO
## License
Hash Bags is distributed under the [MIT License](LICENSE), inheriting from Cake Wallet.
Hash Wallet is distributed under the [MIT License](LICENSE), inheriting from Cake Wallet.
```
Copyright (C) 20182023 Cake Labs LLC
Copyright (C) 2026 Such Software LLC
```
Cake Wallet, the Cake Wallet logo, and related marks are trademarks of Cake Labs LLC and are not used by Hash Bags.
Cake Wallet, the Cake Wallet logo, and related marks are trademarks of Cake Labs LLC and are not used by Hash Wallet.

View File

@@ -60,7 +60,7 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="hashbags"
android:scheme="cakewallet"
android:host="y.at" />
</intent-filter>
<!-- currencies qr code scheme -->

View File

@@ -1,9 +1,4 @@
# AAB universal-bundle packaging (AabFlinger.writeZip) OOMs at 6G because
# parallel ForkJoinPool workers all share the heap while buffering compressed
# native libs. Bumped to 10G + capped worker count to flatten the peak.
# linux-beast Docker container has plenty of RAM; bump higher if needed.
org.gradle.jvmargs=-Xmx10240M -XX:MaxMetaspaceSize=1024M -Dfile.encoding=UTF-8
org.gradle.workers.max=2
org.gradle.jvmargs=-Xmx6144M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -53,6 +53,6 @@
},
{
"question" : "Wie kontaktiere ich den Hash Wallet-Support?",
"answer" : "Senden Sie eine E-Mail an support@such.software, schließen Sie sich dem Telegramm unter @such_software an oder twittern Sie @such_software!\n"
"answer" : "Senden Sie eine E-Mail an support@cakewallet.com, schließen Sie sich dem Telegramm unter @cakewallet_bot an oder twittern Sie @CakeWalletXMR!\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "How do I contact Hash Wallet support?",
"answer" : "Email support@such.software, join the Telegram at @such_software, or tweet @such_software!\n"
"answer" : "Email support@cakewallet.com, join the Telegram at @cakewallet_bot, or tweet @CakeWalletXMR!\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "¿Cómo contacto al soporte de Hash Wallet?",
"answer" : "¡Envíe un correo electrónico a support@such.software, únase al Telegram en @such_software o envíe un tweet a @such_software!\n"
"answer" : "¡Envíe un correo electrónico a support@cakewallet.com, únase al Telegram en @cakewallet_bot o envíe un tweet a @CakeWalletXMR!\n"
}
]

View File

@@ -54,6 +54,6 @@
},
{
"question" : "Comment puis-je contacter le support de Hash Wallet ?",
"answer" : "Envoyez un email à support@such.software, rejoignez le groupe Telegram @such_software, ou @such_software sur Twitter!\n"
"answer" : "Envoyez un email à support@cakewallet.com, rejoignez le groupe Telegram @cakewallet_bot, ou @CakeWalletXMR sur Twitter!\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "मैं केक वॉलेट से कैसे संपर्क करूं?",
"answer" : "ईमेल support@such.software, @such_software पर टेलीग्राम में शामिल हों, या @such_software पर ट्वीट करें!\n"
"answer" : "ईमेल support@cakewallet.com, @cakewallet_bot पर टेलीग्राम में शामिल हों, या @CakeWalletXMR पर ट्वीट करें!\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "Hash Walletサポートに連絡するにはどうすればよいですか",
"answer" : "support@such.softwareにメールを送信するか、@such_softwareで電報に参加するか、@such_softwareにツイートしてください。\n"
"answer" : "support@cakewallet.comにメールを送信するか、@cakewallet_botで電報に参加するか、@CakeWalletXMRにツイートしてください。\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "Hash Wallet 지원팀에 연락하려면 어떻게해야합니까?",
"answer" : "support@such.software로 이메일을 보내거나 @such_software에서 전보에 가입하거나 @such_software을 트윗하십시오!\n"
"answer" : "support@cakewallet.com로 이메일을 보내거나 @cakewallet_bot에서 전보에 가입하거나 @CakeWalletXMR을 트윗하십시오!\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "Hoe neem ik contact op met Hash Wallet-ondersteuning?",
"answer" : "E-mail support@such.software, word lid van het Telegram op @such_software of tweet @such_software!\n"
"answer" : "E-mail support@cakewallet.com, word lid van het Telegram op @cakewallet_bot of tweet @CakeWalletXMR!\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "Jak skontaktować się z obsługą Hash Wallet?",
"answer" : "Wyślij e-mail na adres support@such.software, dołącz do telegramu na @such_software lub tweet @such_software!\n"
"answer" : "Wyślij e-mail na adres support@cakewallet.com, dołącz do telegramu na @cakewallet_bot lub tweet @CakeWalletXMR!\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "Como entro em contato com o suporte da Hash Wallet?",
"answer" : "Envie um e-mail para support@such.software, participe do Telegram em @such_software ou envie um tweet para @such_software!\n"
"answer" : "Envie um e-mail para support@cakewallet.com, participe do Telegram em @cakewallet_bot ou envie um tweet para @CakeWalletXMR!\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "Как мне связаться со службой поддержки Hash Wallet?",
"answer" : "По электронной почте support@such.software, присоединитесь к Telegram по адресу @such_software или отправьте твит @such_software!\n"
"answer" : "По электронной почте support@cakewallet.com, присоединитесь к Telegram по адресу @cakewallet_bot или отправьте твит @CakeWalletXMR!\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "Як мені зв'язатися зі службою підтримки Hash Wallet?",
"answer" : "По електронній пошті support@such.software, приєднайтеся до Telegram за адресою @such_software або надішліть твіт @such_software!\n"
"answer" : "По електронній пошті support@cakewallet.com, приєднайтеся до Telegram за адресою @cakewallet_bot або надішліть твіт @CakeWalletXMR!\n"
}
]

View File

@@ -53,6 +53,6 @@
},
{
"question" : "如何联系蛋糕钱包支持?",
"answer" : "电子邮件support@such.software通过@such_software加入电报或在@such_software上发布推文!\n"
"answer" : "电子邮件support@cakewallet.com通过@cakewallet_bot加入电报或在@CakeWalletXMR上发布推文!\n"
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<title>Hash Bags mark</title>
<desc>The Hash Bags mark: a white hash glyph centered in a brand-green rounded square.</desc>
<title>Hash Wallet mark</title>
<desc>The Hash Wallet mark: a white hash glyph centered in a brand-green rounded square.</desc>
<rect width="100" height="100" rx="22" fill="#1a5c38"/>
<text x="50" y="76" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="74" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
</svg>

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 487 B

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<title>Hash Bags mark</title>
<desc>The Hash Bags mark: a white hash glyph centered in a brand-green rounded square.</desc>
<title>Hash Wallet mark</title>
<desc>The Hash Wallet mark: a white hash glyph centered in a brand-green rounded square.</desc>
<rect width="100" height="100" rx="22" fill="#1a5c38"/>
<text x="50" y="76" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="74" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
</svg>

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 654 B

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 B

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 747 B

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 822 B

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 B

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 925 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<title>Hash Bags mark</title>
<desc>The Hash Bags mark: a white hash glyph centered in a brand-green rounded square.</desc>
<title>Hash Wallet mark</title>
<desc>The Hash Wallet mark: a white hash glyph centered in a brand-green rounded square.</desc>
<rect width="100" height="100" rx="22" fill="#1a5c38"/>
<text x="50" y="76" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="74" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
</svg>

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 487 B

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
<title>Hash Bags app icon</title>
<desc>Hash Bags app icon: brand-green rounded square with a centered white hash.</desc>
<title>Hash Wallet app icon</title>
<desc>Hash Wallet app icon: brand-green rounded square with a centered white hash.</desc>
<rect width="512" height="512" rx="112" fill="#1a5c38"/>
<text x="256" y="390" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="380" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
</svg>

Before

Width:  |  Height:  |  Size: 485 B

After

Width:  |  Height:  |  Size: 489 B

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 100" width="360" height="100">
<title>Hash Bags horizontal lockup</title>
<desc>Hash Bags logo with green-boxed hash mark on the left and Hash Bags wordmark to the right.</desc>
<title>Hash Wallet horizontal lockup</title>
<desc>Hash Wallet logo with green-boxed hash mark on the left and Hash Wallet wordmark to the right.</desc>
<rect x="10" y="18" width="64" height="64" rx="14" fill="#1a5c38"/>
<text x="42" y="68" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="48" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
<text x="90" y="63" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="32" font-weight="500" fill="#1a1a1a">Hash Bags</text>
<text x="90" y="63" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="32" font-weight="500" fill="#1a1a1a">Hash Wallet</text>
</svg>

Before

Width:  |  Height:  |  Size: 674 B

After

Width:  |  Height:  |  Size: 682 B

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 100" width="360" height="100">
<title>Hash Bags horizontal lockup (dark theme)</title>
<desc>Hash Bags logo with green-boxed hash mark on the left and a white wordmark to the right, intended for dark-theme backgrounds.</desc>
<title>Hash Wallet horizontal lockup (dark theme)</title>
<desc>Hash Wallet logo with green-boxed hash mark on the left and a white wordmark to the right, intended for dark-theme backgrounds.</desc>
<rect x="10" y="18" width="64" height="64" rx="14" fill="#1a5c38"/>
<text x="42" y="68" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="48" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
<text x="90" y="63" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="32" font-weight="500" fill="#ffffff">Hash Bags</text>
<text x="90" y="63" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="32" font-weight="500" fill="#ffffff">Hash Wallet</text>
</svg>

Before

Width:  |  Height:  |  Size: 722 B

After

Width:  |  Height:  |  Size: 728 B

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 80" width="380" height="80">
<title>#ash Wallet — inline lockup</title>
<desc>Hash Bags inline lockup where a green-boxed hash mark sits before the word "ash Wallet" to spell out "#ash Wallet".</desc>
<desc>Hash Wallet inline lockup where a green-boxed hash mark sits before the word "ash Wallet" to spell out "#ash Wallet".</desc>
<rect x="6" y="14" width="52" height="52" rx="11" fill="#1a5c38"/>
<text x="32" y="55" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="40" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
<text x="68" y="55" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="42" font-weight="500" fill="#1a1a1a">ash Wallet</text>

Before

Width:  |  Height:  |  Size: 699 B

After

Width:  |  Height:  |  Size: 701 B

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 80" width="380" height="80">
<title>#ash Wallet — inline lockup (dark theme)</title>
<desc>Hash Bags inline lockup spelling "#ash Wallet" with white text, intended for dark-theme backgrounds.</desc>
<desc>Hash Wallet inline lockup spelling "#ash Wallet" with white text, intended for dark-theme backgrounds.</desc>
<rect x="6" y="14" width="52" height="52" rx="11" fill="#1a5c38"/>
<text x="32" y="55" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="40" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
<text x="68" y="55" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="42" font-weight="500" fill="#ffffff">ash Wallet</text>

Before

Width:  |  Height:  |  Size: 697 B

After

Width:  |  Height:  |  Size: 699 B

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 80" width="500" height="80">
<title>H(ash) = Wallet</title>
<desc>Hash Bags function-joke wordmark with green-boxed hash as the function name.</desc>
<desc>Hash Wallet function-joke wordmark with green-boxed hash as the function name.</desc>
<rect x="6" y="14" width="52" height="52" rx="11" fill="#1a5c38"/>
<text x="32" y="55" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="40" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
<text x="68" y="55" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="40" font-weight="400" fill="#888">(</text>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 80" width="500" height="80">
<title>H(ash) = Wallet (dark theme)</title>
<desc>Hash Bags function-joke wordmark with green-boxed hash as the function name, light-on-dark variant. Punctuation/operators in mid-gray that reads on either background; argument and return in white.</desc>
<desc>Hash Wallet function-joke wordmark with green-boxed hash as the function name, light-on-dark variant. Punctuation/operators in mid-gray that reads on either background; argument and return in white.</desc>
<rect x="6" y="14" width="52" height="52" rx="11" fill="#1a5c38"/>
<text x="32" y="55" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="40" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
<text x="68" y="55" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="40" font-weight="400" fill="#aaa">(</text>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
<title>Hash Bags splash animation</title>
<title>Hash Wallet splash animation</title>
<desc>Splash screen: green rounded square scales in, then white hash glyph fades up.</desc>
<!-- Green box scales up from center -->

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
<title>Hash Bags loading pulse</title>
<title>Hash Wallet loading pulse</title>
<desc>Looping loading animation: green-boxed hash mark pulses opacity, suggesting a hash-computing rhythm.</desc>
<rect x="20" y="20" width="160" height="160" rx="36" fill="#1a5c38">

Before

Width:  |  Height:  |  Size: 796 B

After

Width:  |  Height:  |  Size: 798 B

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
<title>Hash Bags transaction confirm</title>
<title>Hash Wallet transaction confirm</title>
<desc>Transaction confirm animation: empty green-outlined box fills with brand green from bottom up, hash glyph appears at completion. Loops for pending state.</desc>
<defs>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<title>Hash Bags mark — outline</title>
<desc>The Hash Bags mark as a green outline + green hash, no fill. For monochrome UI surfaces and tiny sizes where the solid green box overwhelms.</desc>
<title>Hash Wallet mark — outline</title>
<desc>The Hash Wallet mark as a green outline + green hash, no fill. For monochrome UI surfaces and tiny sizes where the solid green box overwhelms.</desc>
<rect x="4" y="4" width="92" height="92" rx="20" fill="none" stroke="#1a5c38" stroke-width="6"/>
<text x="50" y="76" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="74" font-weight="700" fill="#1a5c38" text-anchor="middle">#</text>
</svg>

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 600 B

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 60" width="280" height="60">
<title>Hash Bags wordmark</title>
<desc>The Hash Bags wordmark in mono, no mark — for headers, footers, or surfaces that already display the logo elsewhere. Uses currentColor so the consuming theme picks the tint.</desc>
<text x="0" y="44" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="36" font-weight="500" fill="currentColor">Hash Bags</text>
<title>Hash Wallet wordmark</title>
<desc>The Hash Wallet wordmark in mono, no mark — for headers, footers, or surfaces that already display the logo elsewhere. Uses currentColor so the consuming theme picks the tint.</desc>
<text x="0" y="44" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="36" font-weight="500" fill="currentColor">Hash Wallet</text>
</svg>

Before

Width:  |  Height:  |  Size: 515 B

After

Width:  |  Height:  |  Size: 521 B

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 240" width="200" height="240">
<title>Hash Bags vertical lockup</title>
<desc>Hash Bags logo with green-boxed hash mark on top, wordmark centered below — for splash screens, about screens, vertical-aspect surfaces. Wordmark uses currentColor for theme adaptability.</desc>
<title>Hash Wallet vertical lockup</title>
<desc>Hash Wallet logo with green-boxed hash mark on top, wordmark centered below — for splash screens, about screens, vertical-aspect surfaces. Wordmark uses currentColor for theme adaptability.</desc>
<rect x="50" y="20" width="100" height="100" rx="22" fill="#1a5c38"/>
<text x="100" y="96" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="74" font-weight="700" fill="#ffffff" text-anchor="middle">#</text>
<text x="100" y="180" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="28" font-weight="500" fill="currentColor" text-anchor="middle">Hash Bags</text>
<text x="100" y="180" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="28" font-weight="500" fill="currentColor" text-anchor="middle">Hash Wallet</text>
</svg>

Before

Width:  |  Height:  |  Size: 802 B

After

Width:  |  Height:  |  Size: 808 B

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<title>Hash Bags notification mark</title>
<title>Hash Wallet notification mark</title>
<desc>Single-color silhouette of the hash mark, no background, sized for Android notification tray (24x24 dp). Renders as white-on-system-tint per Android notification icon rules.</desc>
<text x="12" y="20" font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace" font-size="22" font-weight="700" fill="currentColor" text-anchor="middle">#</text>
</svg>

Before

Width:  |  Height:  |  Size: 534 B

After

Width:  |  Height:  |  Size: 536 B

View File

@@ -1,22 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
<title>Hash Bags app icon</title>
<title>Hash Wallet app icon</title>
<!-- Solid green background. Color matches the brand accent in
hash_wallet_logos.svg (#1a5c38). iOS/Android round the corners
automatically; we ship a square fill. -->
<rect width="1024" height="1024" fill="#1a5c38"/>
<!-- # mark, drawn as 4 vector lines centered at (512, 512). Previous
version used <text> with dominant-baseline="central", but the
rendered # was visually biased toward the top because monospaced
font glyph metrics don't put the # at the geometric center of the
em-box. Path-based rendering removes the font dependency
entirely. -->
<g stroke="#ffffff" stroke-width="80" stroke-linecap="square">
<!-- horizontal bars (top, bottom) — at y = 512 ± 100 -->
<line x1="212" y1="412" x2="812" y2="412"/>
<line x1="212" y1="612" x2="812" y2="612"/>
<!-- vertical bars (left, right) — at x = 512 ± 100 -->
<line x1="412" y1="212" x2="412" y2="812"/>
<line x1="612" y1="212" x2="612" y2="812"/>
</g>
<!-- # mark, white, centered. ui-monospace falls back to the platform's
default monospace; the rasterizer bakes this into a PNG so font
availability on user devices doesn't matter. -->
<text x="512" y="512"
text-anchor="middle"
dominant-baseline="central"
font-family="ui-monospace, 'JetBrains Mono', 'Courier New', monospace"
font-size="720"
font-weight="700"
fill="#ffffff">#</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 863 B

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 680 1080" width="680" height="1080">
<title>Hash Bags logo set v2</title>
<desc>Logo concepts for Hash Bags: standalone mark, lockups, H-emphasized variants, and function-style wordmark.</desc>
<title>Hash Wallet logo set v2</title>
<desc>Logo concepts for Hash Wallet: standalone mark, lockups, H-emphasized variants, and function-style wordmark.</desc>
<style>
.label { font-family: ui-monospace, 'JetBrains Mono', 'Courier New', monospace; font-size: 11px; fill: #999; }
@@ -45,19 +45,19 @@
<g transform="translate(80, 320)">
<rect x="0" y="0" width="64" height="64" rx="12" fill="#1a1a1a"/>
<text x="32" y="50" font-family="ui-monospace, monospace" font-size="48" font-weight="700" fill="#fff" text-anchor="middle">#</text>
<text x="84" y="44" class="word-bold" font-size="32">Hash Bags</text>
<text x="84" y="44" class="word-bold" font-size="32">Hash Wallet</text>
</g>
<g transform="translate(80, 410)">
<rect x="0" y="0" width="64" height="64" rx="12" fill="#1a5c38"/>
<text x="32" y="50" font-family="ui-monospace, monospace" font-size="48" font-weight="700" fill="#fff" text-anchor="middle">#</text>
<text x="84" y="44" class="word-bold" font-size="32">Hash Bags</text>
<text x="84" y="44" class="word-bold" font-size="32">Hash Wallet</text>
</g>
<g transform="translate(420, 320)">
<rect x="20" y="0" width="56" height="56" rx="10" fill="#1a1a1a"/>
<text x="48" y="44" font-family="ui-monospace, monospace" font-size="42" font-weight="700" fill="#fff" text-anchor="middle">#</text>
<text x="48" y="84" font-family="ui-monospace, monospace" font-size="18" font-weight="600" fill="#1a1a1a" text-anchor="middle">Hash Bags</text>
<text x="48" y="84" font-family="ui-monospace, monospace" font-size="18" font-weight="600" fill="#1a1a1a" text-anchor="middle">Hash Wallet</text>
</g>
<!-- 03 — H-EMPHASIZED MARK -->

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1019 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1019 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Some files were not shown because too many files have changed in this diff Show More