Compare commits
50 Commits
f1d6f165ea
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86b4040c9b | ||
|
|
2abff1fe06 | ||
|
|
21f3a75aa5 | ||
|
|
a1b3add2a6 | ||
|
|
5ff5ebf910 | ||
|
|
6e6cf33bf1 | ||
|
|
ac294874bf | ||
|
|
52fd1d784f | ||
|
|
be4cf0e49a | ||
|
|
fc813368cc | ||
|
|
2294bfabe7 | ||
|
|
2dd7c84246 | ||
|
|
5a43cc1114 | ||
|
|
91dadfd35e | ||
|
|
4f38322c92 | ||
|
|
5e6a9d5743 | ||
|
|
b6f6f3f4da | ||
|
|
e6195d1e98 | ||
|
|
59d7833668 | ||
|
|
0ad4a4eb8a | ||
|
|
bdbd3388a3 | ||
|
|
3bf9509f08 | ||
|
|
b8ca749e1e | ||
|
|
8c4fbed869 | ||
|
|
1ad70a210a | ||
|
|
713eba2a53 | ||
|
|
bab0d252ee | ||
|
|
20ad49a403 | ||
|
|
a2a9c1b0aa | ||
|
|
bba8a975cf | ||
|
|
a6bb3c595d | ||
|
|
52b8cd090d | ||
|
|
c506679086 | ||
|
|
6c765a6e0e | ||
|
|
ee4ccacab0 | ||
|
|
0aa9f03f77 | ||
|
|
ba5c81e78b | ||
|
|
9f832633b9 | ||
|
|
1c844c58cf | ||
|
|
8f583b14ea | ||
|
|
e277d1ba33 | ||
|
|
a19d0615c8 | ||
|
|
e21430e1d6 | ||
|
|
bf2a35893d | ||
|
|
d21c944f3f | ||
|
|
1e4af3dbde | ||
|
|
71da372d83 | ||
|
|
53919f73c5 | ||
|
|
e8f766dfe6 | ||
|
|
93744af5ba |
67
.github/workflows/build-android.yml
vendored
@@ -1,16 +1,19 @@
|
||||
name: Hash Wallet Android build
|
||||
name: Hash Bags Android build
|
||||
|
||||
# Triggers:
|
||||
# - push to dev/main (validate every commit)
|
||||
# - PRs targeting dev/main (gate merges)
|
||||
# - manual via workflow_dispatch
|
||||
on:
|
||||
push:
|
||||
branches: [dev, main]
|
||||
pull_request:
|
||||
branches: [dev, main]
|
||||
# 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).
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: android-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -39,7 +42,7 @@ jobs:
|
||||
run: |
|
||||
git config --global --add safe.directory '*'
|
||||
git config --global user.email "ci@suchsoftware.com"
|
||||
git config --global user.name "Hash Wallet CI"
|
||||
git config --global user.name "Hash Bags CI"
|
||||
|
||||
# ---- External prebuilt deps (same approach as Linux workflow) --------
|
||||
- name: Fetch prebuilt torch_dart
|
||||
@@ -97,26 +100,50 @@ 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
|
||||
# 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.
|
||||
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 "MISSING: $src — fail loud, the bundle layout may have changed"
|
||||
exit 1
|
||||
echo "SKIP: $src missing (skipping $abi)"
|
||||
ok=false
|
||||
break
|
||||
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 ==="
|
||||
@@ -161,8 +188,19 @@ 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 set — configure Gitea Actions secrets first"
|
||||
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."
|
||||
exit 1
|
||||
fi
|
||||
# Write decoded keystore next to build.gradle (storeFile path
|
||||
@@ -190,15 +228,20 @@ 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).
|
||||
# via Play App Signing). --verbose surfaces the full gradle stacktrace
|
||||
# when FinalizeBundleTask / signReleaseBundle fails — without it the
|
||||
# error is opaque ('FinalizeBundleTask$BundleToolRunnable failed').
|
||||
- name: Build release AAB
|
||||
run: flutter build appbundle --dart-define-from-file=env.json --release
|
||||
run: flutter build appbundle --dart-define-from-file=env.json --release --verbose
|
||||
|
||||
- name: Sanity-check signature on APK
|
||||
run: |
|
||||
|
||||
200
.github/workflows/build-ios-sim.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Hash Wallet iOS Simulator build
|
||||
name: Hash Bags 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,12 +8,16 @@ name: Hash Wallet iOS Simulator build
|
||||
# Phase 2 (separate workflow): full TestFlight pipeline with signing.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev, main]
|
||||
pull_request:
|
||||
branches: [dev, main]
|
||||
# Manual-only for now. Trigger via Actions → "Hash Bags iOS Simulator
|
||||
# build" → Run workflow when you want a build.
|
||||
workflow_dispatch:
|
||||
|
||||
# Cancel in-flight runs when a newer commit lands on the same branch — so a
|
||||
# burst of commits doesn't pile up N copies of the same long build.
|
||||
concurrency:
|
||||
group: ios-sim-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -24,28 +28,115 @@ 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
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
# Self-hosted Mac runner persists Flutter between runs. Only install if
|
||||
# it's not already on PATH or version doesn't match.
|
||||
- name: Check Flutter 3.32.0
|
||||
id: flutter_check
|
||||
run: |
|
||||
if command -v flutter >/dev/null && flutter --version 2>/dev/null | grep -q "3\.32\.0"; then
|
||||
echo "installed=true" >> "$GITHUB_OUTPUT"
|
||||
flutter --version
|
||||
else
|
||||
echo "installed=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Install Flutter 3.32.0 (if missing)
|
||||
if: steps.flutter_check.outputs.installed != 'true'
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: '3.32.0'
|
||||
channel: stable
|
||||
cache: true
|
||||
|
||||
- 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
|
||||
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
|
||||
flutter --version
|
||||
xcodebuild -version
|
||||
pod --version
|
||||
which wget unzip
|
||||
uname -m
|
||||
which curl unzip
|
||||
|
||||
# ---- External prebuilt deps (same as android/linux) ------------------
|
||||
# macOS doesn't ship wget by default; using curl everywhere.
|
||||
- name: Fetch prebuilt torch_dart
|
||||
run: |
|
||||
set -x -e
|
||||
pushd scripts
|
||||
rm -rf torch_dart torch_dart.tar.gz
|
||||
wget -q https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz -O torch_dart.tar.gz
|
||||
curl -fsSL -o torch_dart.tar.gz https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz
|
||||
mkdir torch_dart
|
||||
tar -xzf torch_dart.tar.gz -C torch_dart
|
||||
rm torch_dart.tar.gz
|
||||
@@ -56,17 +147,29 @@ jobs:
|
||||
set -x -e
|
||||
pushd scripts
|
||||
rm -rf reown_flutter reown_flutter.tar.gz
|
||||
wget -q https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz -O reown_flutter.tar.gz
|
||||
curl -fsSL -o reown_flutter.tar.gz https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz
|
||||
mkdir reown_flutter
|
||||
tar -xzf reown_flutter.tar.gz -C reown_flutter
|
||||
rm reown_flutter.tar.gz
|
||||
popd
|
||||
|
||||
- name: Clone BitBox Flutter
|
||||
- name: Clone BitBox Flutter (iOS — skip Android bindings)
|
||||
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
|
||||
./build_bitbox_flutter.sh
|
||||
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) ------------------
|
||||
@@ -77,24 +180,34 @@ jobs:
|
||||
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
|
||||
mkdir -p "scripts/monero_c/release/$MONERO_C_TAG"
|
||||
pushd "scripts/monero_c/release/$MONERO_C_TAG"
|
||||
wget -q https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
|
||||
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 (one level) ==="
|
||||
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"
|
||||
# List anything iOS-y
|
||||
find "$BUNDLE_DIR" -maxdepth 1 -name '*ios*' -o -name '*apple*' 2>/dev/null || true
|
||||
echo "=== bundle top-level ==="
|
||||
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)
|
||||
@@ -136,6 +249,9 @@ 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
|
||||
@@ -156,6 +272,58 @@ 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:
|
||||
|
||||
244
.github/workflows/build-ios-testflight.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Hash Wallet iOS TestFlight build
|
||||
name: Hash Bags 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 —
|
||||
@@ -21,6 +21,10 @@ on:
|
||||
# input for build number or auto-bump.
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ios-testflight-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -31,12 +35,95 @@ 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
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Check Flutter 3.32.0
|
||||
id: flutter_check
|
||||
run: |
|
||||
if command -v flutter >/dev/null && flutter --version 2>/dev/null | grep -q "3\.32\.0"; then
|
||||
echo "installed=true" >> "$GITHUB_OUTPUT"
|
||||
flutter --version
|
||||
else
|
||||
echo "installed=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Install Flutter 3.32.0 (if missing)
|
||||
if: steps.flutter_check.outputs.installed != 'true'
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: '3.32.0'
|
||||
channel: stable
|
||||
cache: true
|
||||
|
||||
- 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
|
||||
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
|
||||
@@ -46,33 +133,62 @@ jobs:
|
||||
uname -m
|
||||
|
||||
# ---- External prebuilt deps -----------------------------------------
|
||||
# macOS doesn't ship wget; using curl everywhere.
|
||||
- name: Fetch prebuilt torch_dart
|
||||
run: |
|
||||
set -x -e
|
||||
pushd scripts
|
||||
rm -rf torch_dart torch_dart.tar.gz
|
||||
wget -q https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz -O torch_dart.tar.gz
|
||||
curl -fsSL -o torch_dart.tar.gz https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz
|
||||
mkdir torch_dart
|
||||
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: |
|
||||
set -x -e
|
||||
pushd scripts
|
||||
rm -rf reown_flutter reown_flutter.tar.gz
|
||||
wget -q https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz -O reown_flutter.tar.gz
|
||||
curl -fsSL -o reown_flutter.tar.gz https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz
|
||||
mkdir reown_flutter
|
||||
tar -xzf reown_flutter.tar.gz -C reown_flutter
|
||||
rm reown_flutter.tar.gz
|
||||
popd
|
||||
|
||||
- name: Clone BitBox Flutter
|
||||
- name: Clone BitBox Flutter (iOS — skip Android bindings)
|
||||
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
|
||||
./build_bitbox_flutter.sh
|
||||
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
|
||||
|
||||
- name: Fetch prebuilt monero_c bundle
|
||||
@@ -82,11 +198,19 @@ jobs:
|
||||
MONERO_C_TAG=$(cd scripts/monero_c && git describe --tags)
|
||||
mkdir -p "scripts/monero_c/release/$MONERO_C_TAG"
|
||||
pushd "scripts/monero_c/release/$MONERO_C_TAG"
|
||||
wget -q https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
|
||||
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
|
||||
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: |
|
||||
@@ -113,6 +237,40 @@ 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 }}
|
||||
@@ -127,9 +285,22 @@ 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')
|
||||
# Import the .p12
|
||||
# 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.
|
||||
P12="$RUNNER_TEMP/dist.p12"
|
||||
echo "$APPLE_DIST_CERT_P12_BASE64" | base64 -d > "$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
|
||||
|
||||
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"
|
||||
@@ -145,7 +316,7 @@ jobs:
|
||||
PROFILES_DIR="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||
mkdir -p "$PROFILES_DIR"
|
||||
PROFILE="$RUNNER_TEMP/Hash_Wallet.mobileprovision"
|
||||
echo "$APPLE_PROVISIONING_PROFILE_BASE64" | base64 -d > "$PROFILE"
|
||||
echo "$APPLE_PROVISIONING_PROFILE_BASE64" | openssl base64 -d -A > "$PROFILE"
|
||||
|
||||
# Parse the .mobileprovision to get its UUID + Name (CMS-decoded plist).
|
||||
PROFILE_UUID=$(security cms -D -i "$PROFILE" | plutil -extract UUID raw -o - -)
|
||||
@@ -166,9 +337,29 @@ jobs:
|
||||
run: |
|
||||
set -e
|
||||
mkdir -p "$HOME/.appstoreconnect/private_keys"
|
||||
echo "$APPLE_API_KEY_P8_BASE64" | base64 -d > "$HOME/.appstoreconnect/private_keys/AuthKey_${APPLE_KEY_ID}.p8"
|
||||
P8="$HOME/.appstoreconnect/private_keys/AuthKey_${APPLE_KEY_ID}.p8"
|
||||
echo "$APPLE_API_KEY_P8_BASE64" | openssl base64 -d -A > "$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: |
|
||||
@@ -181,11 +372,44 @@ 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:
|
||||
|
||||
100
.github/workflows/build-linux.yml
vendored
@@ -1,16 +1,19 @@
|
||||
name: Hash Wallet Linux build
|
||||
name: Hash Bags 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:
|
||||
push:
|
||||
branches: [dev, main]
|
||||
pull_request:
|
||||
branches: [dev, main]
|
||||
# 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.
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: linux-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -44,7 +47,7 @@ jobs:
|
||||
run: |
|
||||
git config --global --add safe.directory '*'
|
||||
git config --global user.email "ci@suchsoftware.com"
|
||||
git config --global user.name "Hash Wallet CI"
|
||||
git config --global user.name "Hash Bags CI"
|
||||
|
||||
# ---- Secrets ---------------------------------------------------------
|
||||
# IMPORTANT: don't pre-write any of the tool/.*secrets-config.json
|
||||
@@ -158,22 +161,93 @@ 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
|
||||
|
||||
- name: Compress release bundle
|
||||
# ---- 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
|
||||
run: |
|
||||
pushd build/linux/x64/release
|
||||
zip -r hash_wallet_linux_${{ github.sha }}.zip bundle
|
||||
popd
|
||||
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
|
||||
|
||||
# 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 artifact
|
||||
- name: Upload AppImage artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: hash-wallet-linux-${{ github.sha }}
|
||||
path: build/linux/x64/release/hash_wallet_linux_*.zip
|
||||
name: hash-wallet-linux-appimage-${{ github.sha }}
|
||||
path: build/linux/x64/release/Hash_Bags-x86_64.AppImage
|
||||
retention-days: 14
|
||||
|
||||
348
.github/workflows/build-windows.yml
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
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
@@ -233,3 +233,7 @@ 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/
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Contributing to Hash Wallet
|
||||
# Contributing to Hash Bags
|
||||
|
||||
Thanks for the interest. Before opening a PR, please read this.
|
||||
|
||||
## Project shape
|
||||
|
||||
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?
|
||||
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?
|
||||
|
||||
## 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 Wallet build flag set. Don't pass `--solana`, `--tron`, `--zano`, `--decred`, or `--zcash` — those packages were deleted.)
|
||||
(That's the canonical Hash Bags 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 Wallet ships with no telemetry by design.
|
||||
- Don't add new top-level analytics. Hash Bags 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 Wallet / 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 Bags / 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 Wallet diff.
|
||||
- Documentation, especially around the build pipeline and the Cake → Hash Bags 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 Wallet 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 Bags 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 Wallet.
|
||||
By contributing, you agree your contribution is licensed under the [MIT License](LICENSE.md), the same as the rest of Hash Bags.
|
||||
|
||||
10
PRIVACY.md
@@ -3,12 +3,12 @@ PRIVACY POLICY
|
||||
|
||||
Last Updated: May 16, 2026
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
1. INFORMATION WE DO NOT COLLECT
|
||||
--------------------------------
|
||||
|
||||
Hash Wallet operates entirely on your device. We do not collect, transmit, store, or have access to:
|
||||
Hash Bags 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 Wallet 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 Wallet must connect to remote blockchain nodes.
|
||||
To fetch balances and broadcast transactions, Hash Bags 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 Wallet integrates with a third-party swap aggregator, Trocador, to allow you to exchange digital assets natively within the interface.
|
||||
Hash Bags 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 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.
|
||||
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.
|
||||
|
||||
5. CONTACT US
|
||||
-------------
|
||||
|
||||
10
README.md
@@ -1,10 +1,10 @@
|
||||
# Hash Wallet
|
||||
# Hash Bags
|
||||
|
||||
**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).
|
||||
**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).
|
||||
|
||||
## Why a fork?
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Supported chains
|
||||
|
||||
@@ -75,11 +75,11 @@ Issues and PRs welcome at https://github.com/Such-Software/hash-wallet. See [`CO
|
||||
|
||||
## License
|
||||
|
||||
Hash Wallet is distributed under the [MIT License](LICENSE), inheriting from Cake Wallet.
|
||||
Hash Bags is distributed under the [MIT License](LICENSE), inheriting from Cake Wallet.
|
||||
|
||||
```
|
||||
Copyright (C) 2018–2023 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 Wallet.
|
||||
Cake Wallet, the Cake Wallet logo, and related marks are trademarks of Cake Labs LLC and are not used by Hash Bags.
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data
|
||||
android:scheme="cakewallet"
|
||||
android:scheme="hashbags"
|
||||
android:host="y.at" />
|
||||
</intent-filter>
|
||||
<!-- currencies qr code scheme -->
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
org.gradle.jvmargs=-Xmx6144M
|
||||
# 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
|
||||
android.enableR8=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "Wie kontaktiere ich den Hash Wallet-Support?",
|
||||
"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"
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "How do I contact Hash Wallet support?",
|
||||
"answer" : "Email support@cakewallet.com, join the Telegram at @cakewallet_bot, or tweet @CakeWalletXMR!\n"
|
||||
"answer" : "Email support@such.software, join the Telegram at @such_software, or tweet @such_software!\n"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "¿Cómo contacto al soporte de Hash Wallet?",
|
||||
"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"
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -54,6 +54,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "Comment puis-je contacter le support de Hash Wallet ?",
|
||||
"answer" : "Envoyez un email à support@cakewallet.com, rejoignez le groupe Telegram @cakewallet_bot, ou @CakeWalletXMR sur Twitter!\n"
|
||||
"answer" : "Envoyez un email à support@such.software, rejoignez le groupe Telegram @such_software, ou @such_software sur Twitter!\n"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "मैं केक वॉलेट से कैसे संपर्क करूं?",
|
||||
"answer" : "ईमेल support@cakewallet.com, @cakewallet_bot पर टेलीग्राम में शामिल हों, या @CakeWalletXMR पर ट्वीट करें!\n"
|
||||
"answer" : "ईमेल support@such.software, @such_software पर टेलीग्राम में शामिल हों, या @such_software पर ट्वीट करें!\n"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "Hash Walletサポートに連絡するにはどうすればよいですか?",
|
||||
"answer" : "support@cakewallet.comにメールを送信するか、@cakewallet_botで電報に参加するか、@CakeWalletXMRにツイートしてください。\n"
|
||||
"answer" : "support@such.softwareにメールを送信するか、@such_softwareで電報に参加するか、@such_softwareにツイートしてください。\n"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "Hash Wallet 지원팀에 연락하려면 어떻게해야합니까?",
|
||||
"answer" : "support@cakewallet.com로 이메일을 보내거나 @cakewallet_bot에서 전보에 가입하거나 @CakeWalletXMR을 트윗하십시오!\n"
|
||||
"answer" : "support@such.software로 이메일을 보내거나 @such_software에서 전보에 가입하거나 @such_software을 트윗하십시오!\n"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "Hoe neem ik contact op met Hash Wallet-ondersteuning?",
|
||||
"answer" : "E-mail support@cakewallet.com, word lid van het Telegram op @cakewallet_bot of tweet @CakeWalletXMR!\n"
|
||||
"answer" : "E-mail support@such.software, word lid van het Telegram op @such_software of tweet @such_software!\n"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "Jak skontaktować się z obsługą Hash Wallet?",
|
||||
"answer" : "Wyślij e-mail na adres support@cakewallet.com, dołącz do telegramu na @cakewallet_bot lub tweet @CakeWalletXMR!\n"
|
||||
"answer" : "Wyślij e-mail na adres support@such.software, dołącz do telegramu na @such_software lub tweet @such_software!\n"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "Como entro em contato com o suporte da Hash Wallet?",
|
||||
"answer" : "Envie um e-mail para support@cakewallet.com, participe do Telegram em @cakewallet_bot ou envie um tweet para @CakeWalletXMR!\n"
|
||||
"answer" : "Envie um e-mail para support@such.software, participe do Telegram em @such_software ou envie um tweet para @such_software!\n"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "Как мне связаться со службой поддержки Hash Wallet?",
|
||||
"answer" : "По электронной почте support@cakewallet.com, присоединитесь к Telegram по адресу @cakewallet_bot или отправьте твит @CakeWalletXMR!\n"
|
||||
"answer" : "По электронной почте support@such.software, присоединитесь к Telegram по адресу @such_software или отправьте твит @such_software!\n"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "Як мені зв'язатися зі службою підтримки Hash Wallet?",
|
||||
"answer" : "По електронній пошті support@cakewallet.com, приєднайтеся до Telegram за адресою @cakewallet_bot або надішліть твіт @CakeWalletXMR!\n"
|
||||
"answer" : "По електронній пошті support@such.software, приєднайтеся до Telegram за адресою @such_software або надішліть твіт @such_software!\n"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,6 +53,6 @@
|
||||
},
|
||||
{
|
||||
"question" : "如何联系蛋糕钱包支持?",
|
||||
"answer" : "电子邮件support@cakewallet.com,通过@cakewallet_bot加入电报,或在@CakeWalletXMR上发布推文!\n"
|
||||
"answer" : "电子邮件support@such.software,通过@such_software加入电报,或在@such_software上发布推文!\n"
|
||||
}
|
||||
]
|
||||
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 5.3 KiB |
@@ -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 Wallet mark</title>
|
||||
<desc>The Hash Wallet mark: a white hash glyph centered in a brand-green rounded square.</desc>
|
||||
<title>Hash Bags mark</title>
|
||||
<desc>The Hash Bags 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: 487 B After Width: | Height: | Size: 483 B |
@@ -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 Wallet mark</title>
|
||||
<desc>The Hash Wallet mark: a white hash glyph centered in a brand-green rounded square.</desc>
|
||||
<title>Hash Bags mark</title>
|
||||
<desc>The Hash Bags 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: 487 B After Width: | Height: | Size: 483 B |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 654 B |
|
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 177 B |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 644 B |
|
Before Width: | Height: | Size: 459 B After Width: | Height: | Size: 172 B |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 747 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 180 B |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 822 B |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 187 B |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 925 B |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 27 KiB |
@@ -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 Wallet mark</title>
|
||||
<desc>The Hash Wallet mark: a white hash glyph centered in a brand-green rounded square.</desc>
|
||||
<title>Hash Bags mark</title>
|
||||
<desc>The Hash Bags 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: 487 B After Width: | Height: | Size: 483 B |
@@ -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 Wallet app icon</title>
|
||||
<desc>Hash Wallet app icon: brand-green rounded square with a centered white hash.</desc>
|
||||
<title>Hash Bags app icon</title>
|
||||
<desc>Hash Bags 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: 489 B After Width: | Height: | Size: 485 B |
@@ -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 Wallet horizontal lockup</title>
|
||||
<desc>Hash Wallet logo with green-boxed hash mark on the left and Hash Wallet wordmark to the right.</desc>
|
||||
<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>
|
||||
<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 Wallet</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>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 682 B After Width: | Height: | Size: 674 B |
@@ -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 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>
|
||||
<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>
|
||||
<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 Wallet</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>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 728 B After Width: | Height: | Size: 722 B |
@@ -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 Wallet inline lockup where a green-boxed hash mark sits before the word "ash Wallet" to spell out "#ash Wallet".</desc>
|
||||
<desc>Hash Bags 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: 701 B After Width: | Height: | Size: 699 B |
@@ -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 Wallet inline lockup spelling "#ash Wallet" with white text, intended for dark-theme backgrounds.</desc>
|
||||
<desc>Hash Bags 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: 699 B After Width: | Height: | Size: 697 B |
@@ -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 Wallet function-joke wordmark with green-boxed hash as the function name.</desc>
|
||||
<desc>Hash Bags 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 |
@@ -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 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>
|
||||
<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>
|
||||
<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 |
@@ -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 Wallet splash animation</title>
|
||||
<title>Hash Bags 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 |
@@ -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 Wallet loading pulse</title>
|
||||
<title>Hash Bags 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: 798 B After Width: | Height: | Size: 796 B |
@@ -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 Wallet transaction confirm</title>
|
||||
<title>Hash Bags 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 |
@@ -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 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>
|
||||
<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>
|
||||
<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: 600 B After Width: | Height: | Size: 596 B |
@@ -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 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>
|
||||
<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>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 521 B After Width: | Height: | Size: 515 B |
@@ -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 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>
|
||||
<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>
|
||||
<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 Wallet</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>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 808 B After Width: | Height: | Size: 802 B |
@@ -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 Wallet notification mark</title>
|
||||
<title>Hash Bags 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: 536 B After Width: | Height: | Size: 534 B |
@@ -1,18 +1,22 @@
|
||||
<?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 Wallet app icon</title>
|
||||
<title>Hash Bags 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, 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>
|
||||
<!-- # 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>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 863 B After Width: | Height: | Size: 1.1 KiB |
@@ -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 Wallet logo set v2</title>
|
||||
<desc>Logo concepts for Hash Wallet: standalone mark, lockups, H-emphasized variants, and function-style wordmark.</desc>
|
||||
<title>Hash Bags logo set v2</title>
|
||||
<desc>Logo concepts for Hash Bags: 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 Wallet</text>
|
||||
<text x="84" y="44" class="word-bold" font-size="32">Hash Bags</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 Wallet</text>
|
||||
<text x="84" y="44" class="word-bold" font-size="32">Hash Bags</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 Wallet</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>
|
||||
</g>
|
||||
|
||||
<!-- 03 — H-EMPHASIZED MARK -->
|
||||
|
||||
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 591 B |
|
Before Width: | Height: | Size: 1019 B |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1019 B |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 325 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 14 KiB |