#!/bin/bash # # vendor-walletcore.sh # OS Vault # # Author: Simon-Pierre Boucher # Mail: contact@spboucher.ai # # Vendors Trust wallet-core as a local SwiftPM package with native macOS # support — the signing engine for the non-EVM expansion (Tron, TON, XRPL, # Solana fallback…). Upstream's SPM package is iOS-only, but the official # CocoaPods tarball ships a macos-arm64_x86_64 slice; this script repackages # it, fully automated (proven working on this machine — see # docs/RESEARCH-MULTICHAIN.md §wallet-core). # # Usage: scripts/vendor-walletcore.sh [version] (default 4.7.2) set -euo pipefail cd "$(dirname "$0")/.." VERSION="${1:-4.7.2}" URL="https://github.com/trustwallet/wallet-core/releases/download/${VERSION}/TrustWalletCore-${VERSION}.tar.xz" DEST="vendor/WalletCoreSPM" WORK="$(mktemp -d)" trap 'rm -rf "$WORK"' EXIT echo "=== Downloading official wallet-core ${VERSION} tarball ===" curl -fL --retry 3 -o "$WORK/twc.tar.xz" "$URL" tar -xJf "$WORK/twc.tar.xz" -C "$WORK" # The tarball extracts flat: include/ Sources/ WalletCoreCommon.xcframework SRC="$WORK" [ -d "$SRC/include/TrustWalletCore" ] && [ -d "$SRC/Sources" ] && [ -d "$SRC/WalletCoreCommon.xcframework" ] || { echo "ERROR: tarball layout unexpected" >&2; exit 1; } echo "=== Assembling local SwiftPM package in $DEST ===" rm -rf "$DEST" mkdir -p "$DEST/Sources/WalletCoreC/include" "$DEST/Sources/WalletCore" # C target: public headers + the Security.framework RNG shim (SECURITY-CRITICAL: # trezor-crypto takes entropy from these consumer-provided symbols). cp -R "$SRC/include/TrustWalletCore" "$DEST/Sources/WalletCoreC/include/" find "$SRC" -name 'SecRandom.m' -exec cp {} "$DEST/Sources/WalletCoreC/" \; [ -f "$DEST/Sources/WalletCoreC/SecRandom.m" ] || { echo "ERROR: SecRandom.m missing" >&2; exit 1; } # Swift wrappers: rename the protobuf module, inject imports SPM needs. cp -R "$SRC/Sources/." "$DEST/Sources/WalletCore/" # Swift target must be Swift-only (SecRandom.m lives in WalletCoreC). find "$DEST/Sources/WalletCore" \( -name '*.m' -o -name '*.h' \) -delete # Global rename: the generated code also uses the module as a type qualifier. find "$DEST/Sources/WalletCore" -name '*.swift' -exec sed -i '' \ -e 's/WalletCoreSwiftProtobuf/SwiftProtobuf/g' {} \; find "$DEST/Sources/WalletCore" -name '*.swift' -exec sed -i '' \ -e '1s/^/import WalletCoreC\nimport Foundation\n/' {} \; # Native binary (contains the macos-arm64_x86_64 slice). cp -R "$SRC/WalletCoreCommon.xcframework" "$DEST/" plutil -p "$DEST/WalletCoreCommon.xcframework/Info.plist" | grep -q macos || { echo "ERROR: no macOS slice in xcframework" >&2; exit 1; } # De-duplicate the Rust runtime: both wallet-core and bdkFFI (Bitcoin Dev Kit) # statically embed Rust's std, and the linker rejects the duplicate global # `_rust_eh_personality`. Demote wallet-core's copy to private in the single # archive member that defines it (surgical — merging the whole archive breaks # unused-member semantics). echo "=== Demoting duplicate _rust_eh_personality in the macOS slice ===" BIN="$DEST/WalletCoreCommon.xcframework/macos-arm64_x86_64/WalletCoreCommon.framework/Versions/A/WalletCoreCommon" FIX="$WORK/rustfix" mkdir -p "$FIX" for arch in arm64 x86_64; do mkdir -p "$FIX/$arch" ( cd "$FIX/$arch" lipo "$BIN" -thin "$arch" -output slice.a MEMBER=$(nm -gU -A slice.a 2>/dev/null | grep " T _rust_eh_personality" | head -1 | cut -d: -f2) [ -n "$MEMBER" ] || { echo "no duplicate in $arch (upstream fixed?) — skipping"; exit 0; } ar -x slice.a "$MEMBER" ld -r -arch "$arch" -platform_version macos 13.0 15.5 "$MEMBER" -o patched.o \ -unexported_symbol _rust_eh_personality 2>/dev/null mv patched.o "$MEMBER" ar -r -s slice.a "$MEMBER" 2>/dev/null ) done lipo -create "$FIX/arm64/slice.a" "$FIX/x86_64/slice.a" -output "$BIN" cat > "$DEST/Package.swift" <<'EOF' // swift-tools-version:5.10 // Auto-generated by scripts/vendor-walletcore.sh — do not edit by hand. // The prebuilt static framework is exposed as a local binaryTarget (no // unsafeFlags — those would make the package unusable as a dependency). import PackageDescription let package = Package( name: "WalletCoreSPM", platforms: [.macOS(.v13)], products: [ .library(name: "WalletCore", targets: ["WalletCore"]) ], dependencies: [ .package(url: "https://github.com/apple/swift-protobuf.git", from: "1.29.0") ], targets: [ .binaryTarget( name: "WalletCoreBinary", path: "WalletCoreCommon.xcframework" ), .target( name: "WalletCoreC", dependencies: ["WalletCoreBinary"], path: "Sources/WalletCoreC", publicHeadersPath: "include" ), .target( name: "WalletCore", dependencies: [ "WalletCoreC", .product(name: "SwiftProtobuf", package: "swift-protobuf") ], path: "Sources/WalletCore", linkerSettings: [ .linkedLibrary("c++"), .linkedFramework("Security") ] ) ] ) EOF echo "=== Done ===" echo "Vendored wallet-core ${VERSION} → $DEST" echo "Add to Package.swift deps: .package(path: \"vendor/WalletCoreSPM\") and" echo "product .product(name: \"WalletCore\", package: \"WalletCoreSPM\")."