Merge branch 'main' into ui-enhancements

This commit is contained in:
tuxsudo 2025-01-22 23:20:21 -05:00 committed by GitHub
commit ce0d7da97a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
101 changed files with 1452 additions and 653 deletions
.github/workflows
assets/images
build-guide-linux.mdbuild-guide-win.md
cw_core/lib
cw_monero/lib
integration_test
integration_test_runner.sh
lib
linux
model_generator.shpubspec_base.yaml
res/values
scripts

View file

@ -1,84 +0,0 @@
name: Cache Dependencies
on:
workflow_dispatch:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-20.04
steps:
- name: Free Disk Space (Ubuntu)
uses: insightsengineering/disk-space-reclaimer@v1
with:
tools-cache: true
android: false
dotnet: true
haskell: true
large-packages: true
swap-storage: true
docker-images: true
- uses: actions/checkout@v2
- uses: actions/setup-java@v2
with:
distribution: "temurin"
java-version: "17"
- name: Configure placeholder git details
run: |
git config --global user.email "CI@cakewallet.com"
git config --global user.name "Cake Github Actions"
- name: Flutter action
uses: subosito/flutter-action@v1
with:
flutter-version: "3.24.4"
channel: stable
- name: Install package dependencies
run: sudo apt-get install -y curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang
- name: Execute Build and Setup Commands
run: |
sudo mkdir -p /opt/android
sudo chown $USER /opt/android
cd /opt/android
-y curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install cargo-ndk
git clone https://github.com/cake-tech/cake_wallet.git --branch main
cd cake_wallet/scripts/android/
./install_ndk.sh
source ./app_env.sh cakewallet
chmod +x pubspec_gen.sh
./app_config.sh
- name: Cache Externals
id: cache-externals
uses: actions/cache@v3
with:
path: |
/opt/android/cake_wallet/cw_haven/android/.cxx
/opt/android/cake_wallet/scripts/monero_c/release
key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }}
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
name: Generate Externals
run: |
cd /opt/android/cake_wallet/scripts/android/
source ./app_env.sh cakewallet
./build_monero_all.sh
- name: Cache Keystore
id: cache-keystore
uses: actions/cache@v3
with:
path: /opt/android/cake_wallet/android/app
key: keystore
- if: ${{ steps.cache-keystore.outputs.cache-hit != 'true' }}
name: Generate KeyStore
run: |
cd /opt/android/cake_wallet/android/app
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias testKey -noprompt -dname "CN=CakeWallet, OU=CakeWallet, O=CakeWallet, L=Florida, S=America, C=USA" -storepass $STORE_PASS -keypass $KEY_PASS

View file

@ -1,169 +1,93 @@
name: PR Test Build
name: Cake Wallet Android
on:
pull_request:
branches: [main]
workflow_dispatch:
inputs:
branch:
description: "Branch name to build"
required: true
default: "main"
on: [push]
defaults:
run:
shell: bash
jobs:
PR_test_build:
runs-on: ubuntu-20.04
runs-on: linux-amd64
container:
image: ghcr.io/cake-tech/cake_wallet:main-linux
env:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
MONEROC_CACHE_DIR_ROOT: /opt/generic_cache
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
ANDROID_AVD_HOME: /root/.android/avd
volumes:
- /opt/cw_cache_android/root/.cache:/root/.cache
- /opt/cw_cache_android/root/.android/avd/:/root/.android/avd
- /opt/cw_cache_android/root/.ccache:/root/.ccache
- /opt/cw_cache_android/root/.pub-cache/:/root/.pub-cache
- /opt/cw_cache_android/root/.gradle/:/root/.gradle
- /opt/cw_cache_android/root/.android/:/root/.android
- /opt/cw_cache_android/root/go/pkg:/root/go/pkg
- /opt/cw_cache_android/opt/generic_cache:/opt/generic_cache
- /dev/kvm:/dev/kvm
strategy:
matrix:
api-level: [29]
env:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
PR_NUMBER: ${{ github.event.number }}
steps:
- name: is pr
if: github.event_name == 'pull_request'
run: echo "BRANCH_NAME=${GITHUB_HEAD_REF}" >> $GITHUB_ENV
- name: is not pr
if: github.event_name != 'pull_request'
run: echo "BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_ENV
- name: Free Disk Space (Ubuntu)
uses: insightsengineering/disk-space-reclaimer@v1
with:
tools-cache: true
android: false
dotnet: true
haskell: true
large-packages: true
swap-storage: true
docker-images: true
- uses: actions/checkout@v2
- uses: actions/setup-java@v2
with:
distribution: "temurin"
java-version: "17"
- name: Configure placeholder git details
- name: Fix github actions messing up $HOME...
run: 'echo HOME=/root | sudo tee -a $GITHUB_ENV'
- uses: actions/checkout@v4
- name: configure git
run: |
git config --global user.email "CI@cakewallet.com"
git config --global user.name "Cake Github Actions"
- name: Flutter action
uses: subosito/flutter-action@v1
with:
flutter-version: "3.24.0"
channel: stable
- name: Install package dependencies
run: |
sudo apt update
sudo apt-get install -y curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang
- name: Clone Repo
run: |
sudo mkdir -p /opt/android
sudo chown $USER /opt/android
cd /opt/android
git clone https://github.com/cake-tech/cake_wallet.git --branch ${{ env.BRANCH_NAME }}
# - name: Cache Keystore
# id: cache-keystore
# uses: actions/cache@v3
# with:
# path: /opt/android/cake_wallet/android/app
# key: keystore
#
# - if: ${{ steps.cache-keystore.outputs.cache-hit != 'true' }}
- name: Generate KeyStore
run: |
cd /opt/android/cake_wallet/android/app
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias testKey -noprompt -dname "CN=CakeWallet, OU=CakeWallet, O=CakeWallet, L=Florida, S=America, C=USA" -storepass $STORE_PASS -keypass $KEY_PASS
- name: Execute Build and Setup Commands
run: |
cd /opt/android
-y curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install cargo-ndk
cd cake_wallet/scripts/android/
./install_ndk.sh
source ./app_env.sh cakewallet
chmod +x pubspec_gen.sh
./app_config.sh
- name: Cache Externals
id: cache-externals
uses: actions/cache@v3
with:
path: |
/opt/android/cake_wallet/cw_haven/android/.cxx
/opt/android/cake_wallet/scripts/monero_c/release
key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }}
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
name: Generate Externals
run: |
cd /opt/android/cake_wallet/scripts/android/
source ./app_env.sh cakewallet
./build_monero_all.sh
- name: Install Flutter dependencies
run: |
cd /opt/android/cake_wallet
flutter pub get
- name: Install go and gomobile
run: |
# install go > 1.23:
wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
export PATH=$PATH:~/go/bin
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init
- name: Build mwebd
run: |
# paths are reset after each step, so we need to set them again:
export PATH=$PATH:/usr/local/go/bin
export PATH=$PATH:~/go/bin
cd /opt/android/cake_wallet/scripts/android/
./build_mwebd.sh --dont-install
- name: Generate key properties
run: |
cd /opt/android/cake_wallet
dart run tool/generate_android_key_properties.dart keyAlias=testKey storeFile=key.jks storePassword=$STORE_PASS keyPassword=$KEY_PASS
- name: Generate localization
run: |
cd /opt/android/cake_wallet
dart run tool/generate_localization.dart
- name: Build generated code
run: |
cd /opt/android/cake_wallet
./model_generator.sh
git config --global user.email "ci@cakewallet.com"
git config --global user.name "CakeWallet CI"
- name: Add secrets
run: |
cd /opt/android/cake_wallet
touch lib/.secrets.g.dart
touch cw_evm/lib/.secrets.g.dart
touch cw_solana/lib/.secrets.g.dart
touch cw_core/lib/.secrets.g.dart
touch cw_nano/lib/.secrets.g.dart
touch cw_tron/lib/.secrets.g.dart
echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart
echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart
echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart
echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart
echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
if [[ "x${{ secrets.SALT }}" == "x" ]];
then
echo "const salt = '954f787f12622067f7e548d9450c3832';" > lib/.secrets.g.dart
else
echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart
fi
if [[ "x${{ secrets.KEY_CHAIN_SALT }}" == "x" ]];
then
echo "const keychainSalt = '2d2beba777dbf7dff7013b7a';" >> lib/.secrets.g.dart
else
echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
fi
if [[ "x${{ secrets.KEY }}" == "x" ]];
then
echo "const key = '638e98820ec10a2945e968435c9397a3';" >> lib/.secrets.g.dart
else
echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart
fi
if [[ "x${{ secrets.WALLET_SALT }}" == "x" ]];
then
echo "const walletSalt = '8f7f1b70';" >> lib/.secrets.g.dart
else
echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart
fi
if [[ "x${{ secrets.SHORT_KEY }}" == "x" ]];
then
echo "const shortKey = '653f270c2c152bc7ec864afe';" >> lib/.secrets.g.dart
else
echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart
fi
if [[ "x${{ secrets.BACKUP_SALT }}" == "x" ]];
then
echo "const backupSalt = 'bf630d24ff0b6f60';" >> lib/.secrets.g.dart
else
echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart
fi
if [[ "x${{ secrets.BACKUP_KEY_CHAIN_SALT }}" == "x" ]];
then
echo "const backupKeychainSalt = 'bf630d24ff0b6f60';" >> lib/.secrets.g.dart
else
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
fi
echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
@ -213,86 +137,153 @@ jobs:
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart
# for tests
echo "const moneroTestWalletSeeds ='${{ secrets.MONERO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const moneroLegacyTestWalletSeeds = '${{ secrets.MONERO_LEGACY_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const bitcoinTestWalletSeeds = '${{ secrets.BITCOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const ethereumTestWalletSeeds = '${{ secrets.ETHEREUM_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const wowneroTestWalletSeeds = '${{ secrets.WOWNERO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const moneroTestWalletReceiveAddress = '${{ secrets.MONERO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const bitcoinTestWalletReceiveAddress = '${{ secrets.BITCOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const ethereumTestWalletReceiveAddress = '${{ secrets.ETHEREUM_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const wowneroTestWalletReceiveAddress = '${{ secrets.WOWNERO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const moneroTestWalletBlockHeight = '${{ secrets.MONERO_TEST_WALLET_BLOCK_HEIGHT }}';" >> lib/.secrets.g.dart
- name: prepare monero_c and cache
run: |
export MONEROC_HASH=$(cat scripts/prepare_moneroc.sh | grep 'git checkout' | xargs | awk '{ print $3 }')
echo MONEROC_HASH=$MONEROC_HASH >> /etc/environment
mkdir -p "$MONEROC_CACHE_DIR_ROOT/moneroc-$MONEROC_HASH/monero_c"
pushd scripts
ln -s "$MONEROC_CACHE_DIR_ROOT/moneroc-$MONEROC_HASH/monero_c"
./prepare_moneroc.sh
popd
pushd scripts/monero_c
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/built" || true
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/built" || true
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/built" || true
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/sources" || true
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/sources" || true
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/sources" || true
rm -rf "$PWD/contrib/depends/built" "$PWD/monero/contrib/depends/built" "$PWD/wownero/contrib/depends/built"
rm -rf "$PWD/contrib/depends/sources" "$PWD/monero/contrib/depends/sources" "$PWD/wownero/contrib/depends/sources"
mkdir -p contrib/depends || true
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/built" "$PWD/contrib/depends/built"
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/built" "$PWD/monero/contrib/depends/built"
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/built" "$PWD/wownero/contrib/depends/built"
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/sources" "$PWD/contrib/depends/sources"
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/sources" "$PWD/monero/contrib/depends/sources"
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/sources" "$PWD/wownero/contrib/depends/sources"
popd
- name: Generate KeyStore
run: |
pushd /opt/generic_cache
if [[ ! -f key.jks ]];
then
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias testKey -noprompt -dname "CN=CakeWallet, OU=CakeWallet, O=CakeWallet, L=Florida, S=America, C=USA" -storepass $STORE_PASS -keypass $KEY_PASS
else
echo "$PWD/key.jks exist, not generating"
fi
popd
cp /opt/generic_cache/key.jks android/app
- name: Execute Build and Setup Commands
run: |
pushd scripts/android
source ./app_env.sh cakewallet
./app_config.sh
popd
- name: Build monero_c
run: |
pushd scripts/android/
source ./app_env.sh cakewallet
./build_monero_all.sh
popd
- name: Install Flutter dependencies
run: |
flutter pub get
- name: Build mwebd
run: |
set -x -e
export MWEBD_HASH=$(cat scripts/android/build_mwebd.sh | grep 'git reset --hard' | xargs | awk '{ print $4 }')
echo MWEBD_HASH=$MWEBD_HASH >> /etc/environment
pushd scripts/android
gomobile init;
./build_mwebd.sh --dont-install
popd
- name: Build generated code
run: |
./model_generator.sh async
- name: Generate key properties
run: |
dart run tool/generate_android_key_properties.dart keyAlias=testKey storeFile=key.jks storePassword=$STORE_PASS keyPassword=$KEY_PASS
- name: Generate localization
run: |
dart run tool/generate_localization.dart
- name: Rename app
run: |
echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties
sanitized_branch_name=${BRANCH_NAME#origin/} # Remove 'origin/' prefix if it exists
sanitized_branch_name=${sanitized_branch_name:0:16} # Take only the first 16 characters
sanitized_branch_name=$(echo "$sanitized_branch_name" | tr '[:upper:]' '[:lower:]') # Convert to lowercase
sanitized_branch_name=$(echo "$sanitized_branch_name" | sed 's/[^a-z0-9]//g') # Remove all special characters
# Step 3: Download previous build number
- name: Download previous build number
id: download-build-number
run: |
# Download the artifact if it exists
if [[ ! -f build_number.txt ]]; then
echo "1" > build_number.txt
fi
# Step 4: Read and Increment Build Number
- name: Increment Build Number
id: increment-build-number
run: |
# Read current build number from file
BUILD_NUMBER=$(cat build_number.txt)
BUILD_NUMBER=$((BUILD_NUMBER + 1))
echo "New build number: $BUILD_NUMBER"
# Save the incremented build number
echo "$BUILD_NUMBER" > build_number.txt
# Export the build number to use in later steps
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
# Step 5: Update pubspec.yaml with new build number
- name: Update build number
run: |
cd /opt/android/cake_wallet
sed -i "s/^version: .*/version: 1.0.$BUILD_NUMBER/" pubspec.yaml
echo -e "id=com.cakewallet.test_${sanitized_branch_name}\nname=${BRANCH_NAME}" > android/app.properties
- name: Build
run: |
cd /opt/android/cake_wallet
flutter build apk --release --split-per-abi
# - name: Push to App Center
# run: |
# echo 'Installing App Center CLI tools'
# npm install -g appcenter-cli
# echo "Publishing test to App Center"
# appcenter distribute release \
# --group "Testers" \
# --file "/opt/android/cake_wallet/build/app/outputs/apk/release/app-release.apk" \
# --release-notes ${{ env.BRANCH_NAME }} \
# --app Cake-Labs/Cake-Wallet \
# --token ${{ secrets.APP_CENTER_TOKEN }} \
# --quiet
- name: Rename apk file
run: |
cd /opt/android/cake_wallet/build/app/outputs/flutter-apk
cd build/app/outputs/flutter-apk
mkdir test-apk
cp app-arm64-v8a-release.apk test-apk/${{env.BRANCH_NAME}}.apk
cp app-x86_64-release.apk test-apk/${{env.BRANCH_NAME}}_x86.apk
cp app-arm64-v8a-release.apk test-apk/${BRANCH_NAME}.apk
cp app-x86_64-release.apk test-apk/${BRANCH_NAME}_x86.apk
cd test-apk
cp ${BRANCH_NAME}.apk ${BRANCH_NAME}_slack.apk
- name: Upload Artifact
uses: kittaakos/upload-artifact-as-is@v0
with:
path: /opt/android/cake_wallet/build/app/outputs/flutter-apk/test-apk/
# Re-upload updated build number for the next run
- name: Upload updated build number
uses: actions/upload-artifact@v3
with:
name: build_number
path: build_number.txt
- name: Send Test APK
- name: Find APK file
id: find_apk
run: |
set -x
apk_file=$(ls build/app/outputs/flutter-apk/test-apk/*_slack.apk || exit 1)
echo "APK_FILE=$apk_file" >> $GITHUB_ENV
- name: Upload artifact to slack
if: ${{ !contains(github.event.head_commit.message, 'skip slack') }}
continue-on-error: true
uses: adrey/slack-file-upload-action@1.0.5
with:
token: ${{ secrets.SLACK_APP_TOKEN }}
path: /opt/android/cake_wallet/build/app/outputs/flutter-apk/test-apk/${{env.BRANCH_NAME}}.apk
path: ${{ env.APK_FILE }}
channel: ${{ secrets.SLACK_APK_CHANNEL }}
title: "${{ env.BRANCH_NAME }}.apk"
filename: ${{ env.BRANCH_NAME }}.apk
initial_comment: ${{ github.event.head_commit.message }}
- name: cleanup
run: rm -rf build/app/outputs/flutter-apk/test-apk/
- name: Upload Artifact to github
uses: actions/upload-artifact@v4
with:
path: ${{ github.workspace }}/build/app/outputs/flutter-apk
name: "android apk"

View file

@ -1,139 +1,89 @@
name: PR Test Build linux
name: Cake Wallet Linux
on:
pull_request:
branches: [main]
workflow_dispatch:
inputs:
branch:
description: "Branch name to build"
required: true
default: "main"
on: [push]
defaults:
run:
shell: bash
jobs:
PR_test_build:
runs-on: ubuntu-20.04
env:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
PR_NUMBER: ${{ github.event.number }}
runs-on: linux-amd64
container:
image: ghcr.io/cake-tech/cake_wallet:main-linux
env:
STORE_PASS: test@cake_wallet
KEY_PASS: test@cake_wallet
MONEROC_CACHE_DIR_ROOT: /opt/generic_cache
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
DESKTOP_FORCE_MOBILE: Y
volumes:
- /opt/cw_cache_linux/root/.cache:/root/.cache
- /opt/cw_cache_linux/root/.ccache:/root/.ccache
- /opt/cw_cache_linux/root/.pub-cache/:/root/.pub-cache
- /opt/cw_cache_linux/root/go/pkg:/root/go/pkg
- /opt/cw_cache_linux/opt/generic_cache:/opt/generic_cache
strategy:
matrix:
api-level: [29]
steps:
- name: is pr
if: github.event_name == 'pull_request'
run: echo "BRANCH_NAME=${GITHUB_HEAD_REF}" >> $GITHUB_ENV
- name: is not pr
if: github.event_name != 'pull_request'
run: echo "BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_ENVg
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: "17.x"
- name: Configure placeholder git details
- name: Fix github actions messing up $HOME...
run: 'echo HOME=/root | sudo tee -a $GITHUB_ENV'
- uses: actions/checkout@v4
- name: configure git
run: |
git config --global user.email "CI@cakewallet.com"
git config --global user.name "Cake Github Actions"
- name: Flutter action
uses: subosito/flutter-action@v1
with:
flutter-version: "3.24.0"
channel: stable
- name: Install package dependencies
run: |
sudo apt update
sudo apt-get install -y curl unzip automake build-essential file pkg-config git python-is-python3 libtool libtinfo5 cmake clang
- name: Install desktop dependencies
run: |
sudo apt update
sudo apt install -y ninja-build libgtk-3-dev gperf
- name: Execute Build and Setup Commands
run: |
sudo mkdir -p /opt/android
sudo chown $USER /opt/android
cd /opt/android
-y curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install cargo-ndk
git clone https://github.com/cake-tech/cake_wallet.git --branch ${{ env.BRANCH_NAME }}
cd scripts && ./gen_android_manifest.sh && cd ..
cd cake_wallet/scripts/android/
source ./app_env.sh cakewallet
./app_config.sh
cd ../../..
cd cake_wallet/scripts/linux/
source ./app_env.sh cakewallet
./app_config.sh
cd ../../..
- name: Cache Externals
id: cache-externals
uses: actions/cache@v3
with:
path: |
/opt/android/cake_wallet/cw_haven/android/.cxx
/opt/android/cake_wallet/scripts/monero_c/release
key: linux_${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }}
- if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }}
name: Generate Externals
run: |
cd /opt/android/cake_wallet/scripts/linux/
source ./app_env.sh cakewallet
./build_monero_all.sh
- name: Install Flutter dependencies
run: |
cd /opt/android/cake_wallet
flutter pub get
- name: Install go and gomobile
run: |
# install go > 1.23:
wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
export PATH=$PATH:~/go/bin
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init
- name: Build mwebd
run: |
# paths are reset after each step, so we need to set them again:
export PATH=$PATH:/usr/local/go/bin
export PATH=$PATH:~/go/bin
# build mwebd:
cd /opt/android/cake_wallet/scripts/android/
./build_mwebd.sh --dont-install
- name: Generate localization
run: |
cd /opt/android/cake_wallet
dart run tool/generate_localization.dart
- name: Build generated code
run: |
cd /opt/android/cake_wallet
./model_generator.sh
git config --global user.email "ci@cakewallet.com"
git config --global user.name "CakeWallet CI"
- name: Add secrets
run: |
cd /opt/android/cake_wallet
touch lib/.secrets.g.dart
touch cw_evm/lib/.secrets.g.dart
touch cw_solana/lib/.secrets.g.dart
touch cw_core/lib/.secrets.g.dart
touch cw_nano/lib/.secrets.g.dart
touch cw_tron/lib/.secrets.g.dart
echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart
echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart
echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart
echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart
echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
if [[ "x${{ secrets.SALT }}" == "x" ]];
then
echo "const salt = '954f787f12622067f7e548d9450c3832';" > lib/.secrets.g.dart
else
echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart
fi
if [[ "x${{ secrets.KEY_CHAIN_SALT }}" == "x" ]];
then
echo "const keychainSalt = '2d2beba777dbf7dff7013b7a';" >> lib/.secrets.g.dart
else
echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
fi
if [[ "x${{ secrets.KEY }}" == "x" ]];
then
echo "const key = '638e98820ec10a2945e968435c9397a3';" >> lib/.secrets.g.dart
else
echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart
fi
if [[ "x${{ secrets.WALLET_SALT }}" == "x" ]];
then
echo "const walletSalt = '8f7f1b70';" >> lib/.secrets.g.dart
else
echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart
fi
if [[ "x${{ secrets.SHORT_KEY }}" == "x" ]];
then
echo "const shortKey = '653f270c2c152bc7ec864afe';" >> lib/.secrets.g.dart
else
echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart
fi
if [[ "x${{ secrets.BACKUP_SALT }}" == "x" ]];
then
echo "const backupSalt = 'bf630d24ff0b6f60';" >> lib/.secrets.g.dart
else
echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart
fi
if [[ "x${{ secrets.BACKUP_KEY_CHAIN_SALT }}" == "x" ]];
then
echo "const backupKeychainSalt = 'bf630d24ff0b6f60';" >> lib/.secrets.g.dart
else
echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart
fi
echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart
echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart
@ -144,8 +94,6 @@ jobs:
echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart
echo "const simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> lib/.secrets.g.dart
echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart
echo "const anypayToken = '${{ secrets.ANY_PAY_TOKEN }}';" >> lib/.secrets.g.dart
echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart
@ -156,8 +104,11 @@ jobs:
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
@ -165,7 +116,6 @@ jobs:
echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart
echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart
echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart
echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart
@ -178,39 +128,172 @@ jobs:
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
echo "const meldTestApiKey = '${{ secrets.MELD_TEST_API_KEY }}';" >> lib/.secrets.g.dart
echo "const meldTestPublicKey = '${{ secrets.MELD_TEST_PUBLIC_KEY}}';" >> lib/.secrets.g.dar
echo "const meldTestPublicKey = '${{ secrets.MELD_TEST_PUBLIC_KEY}}';" >> lib/.secrets.g.dart
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart
- name: Rename app
# tests
echo "const moneroTestWalletSeeds ='${{ secrets.MONERO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const moneroLegacyTestWalletSeeds = '${{ secrets.MONERO_LEGACY_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const bitcoinTestWalletSeeds = '${{ secrets.BITCOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const ethereumTestWalletSeeds = '${{ secrets.ETHEREUM_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const wowneroTestWalletSeeds = '${{ secrets.WOWNERO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart
echo "const moneroTestWalletReceiveAddress = '${{ secrets.MONERO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const bitcoinTestWalletReceiveAddress = '${{ secrets.BITCOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const ethereumTestWalletReceiveAddress = '${{ secrets.ETHEREUM_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const wowneroTestWalletReceiveAddress = '${{ secrets.WOWNERO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart
echo "const moneroTestWalletBlockHeight = '${{ secrets.MONERO_TEST_WALLET_BLOCK_HEIGHT }}';" >> lib/.secrets.g.dart
- name: prepare monero_c and cache
run: |
echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties
export MONEROC_HASH=$(cat scripts/prepare_moneroc.sh | grep 'git checkout' | xargs | awk '{ print $3 }')
echo MONEROC_HASH=$MONEROC_HASH >> /etc/environment
mkdir -p "$MONEROC_CACHE_DIR_ROOT/moneroc-$MONEROC_HASH/monero_c"
pushd scripts
ln -s "$MONEROC_CACHE_DIR_ROOT/moneroc-$MONEROC_HASH/monero_c"
./prepare_moneroc.sh
popd
pushd scripts/monero_c
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/built" || true
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/built" || true
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/built" || true
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/sources" || true
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/sources" || true
mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/sources" || true
- name: Build
rm -rf "$PWD/contrib/depends/built" "$PWD/monero/contrib/depends/built" "$PWD/wownero/contrib/depends/built"
rm -rf "$PWD/contrib/depends/sources" "$PWD/monero/contrib/depends/sources" "$PWD/wownero/contrib/depends/sources"
mkdir -p contrib/depends || true
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/built" "$PWD/contrib/depends/built"
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/built" "$PWD/monero/contrib/depends/built"
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/built" "$PWD/wownero/contrib/depends/built"
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/sources" "$PWD/contrib/depends/sources"
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/sources" "$PWD/monero/contrib/depends/sources"
ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/sources" "$PWD/wownero/contrib/depends/sources"
popd
- name: Execute Build and Setup Commands
run: |
pushd scripts/linux
source ./app_env.sh cakewallet
./app_config.sh
popd
- name: Build monero_c
run: |
pushd scripts/linux/
source ./app_env.sh cakewallet
./build_monero_all.sh
popd
- name: Install Flutter dependencies
run: |
flutter pub get
- name: Build generated code
run: |
./model_generator.sh async
- name: Generate localization
run: |
dart run tool/generate_localization.dart
- name: Build linux
run: |
cd /opt/android/cake_wallet
flutter build linux --release
- name: Prepare release zip file
- name: Compress release
run: |
cd /opt/android/cake_wallet/build/linux/x64/release
zip -r ${{env.BRANCH_NAME}}.zip bundle
pushd build/linux/x64/release
zip -r cakewallet_linux.zip bundle
popd
- name: Upload Artifact
uses: kittaakos/upload-artifact-as-is@v0
- name: Upload Artifact to github
uses: actions/upload-artifact@v4
with:
path: /opt/android/cake_wallet/build/linux/x64/release/${{env.BRANCH_NAME}}.zip
path: ${{ github.workspace }}/build/linux/x64/release/cakewallet_linux.zip
name: cakewallet_linux
# Just as an artifact would be enough
# - name: Send Test APK
# continue-on-error: true
# uses: adrey/slack-file-upload-action@1.0.5
# with:
# token: ${{ secrets.SLACK_APP_TOKEN }}
# path: /opt/android/cake_wallet/build/linux/x64/release/${{env.BRANCH_NAME}}.zip
# channel: ${{ secrets.SLACK_APK_CHANNEL }}
# title: "${{ env.BRANCH_NAME }}_linux.zip"
# filename: ${{ env.BRANCH_NAME }}_linux.zip
# initial_comment: ${{ github.event.head_commit.message }}
- name: Prepare virtual desktop
if: ${{ contains(github.event.head_commit.message, 'run tests') }}
run: |
nohup Xvfb :99 -screen 0 720x1280x16 &
echo DISPLAY=:99 | sudo tee -a $GITHUB_ENV
dbus-daemon --system --fork
nohup NetworkManager &
nohup ffmpeg -framerate 60 -video_size 720x1280 -f x11grab -i :99 -c:v libx264 -c:a aac /opt/screen_grab.mkv &
# Note for people adding tests:
# - Tests are ran on Linux, with some things being mocked out.
# - Screen recording is being provided for the entire length of the test, you can download it in github articats.
# - Screen recordeding is encrypted, look at step "Stop screen recording, encrypt and upload", and add your key if you want
# Reason for encryption is the fact that we restore the wallet from seed, and we don't want to leak that, while there
# isn't much in those wallets anyway, we still wouldn't like to leak it to anyone who is able to access github.
- name: Test [confirm_seeds_flow_test]
if: ${{ contains(github.event.head_commit.message, 'run tests') }}
timeout-minutes: 20
run: |
xmessage -timeout 30 "confirm_seeds_flow_test" &
rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet
exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/confirm_seeds_flow_test.dart
- name: Test [create_wallet_flow_test]
if: ${{ contains(github.event.head_commit.message, 'run tests') }}
timeout-minutes: 20
run: |
xmessage -timeout 30 "create_wallet_flow_test" &
rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet
exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/create_wallet_flow_test.dart
- name: Test [exchange_flow_test]
if: ${{ contains(github.event.head_commit.message, 'run tests') }}
timeout-minutes: 20
run: |
xmessage -timeout 30 "exchange_flow_test" &
rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet
exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/exchange_flow_test.dart
- name: Test [restore_wallet_through_seeds_flow_test]
if: ${{ contains(github.event.head_commit.message, 'run tests') }}
timeout-minutes: 20
run: |
xmessage -timeout 30 "restore_wallet_through_seeds_flow_test" &
rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet
exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/restore_wallet_through_seeds_flow_test.dart
- name: Stop screen recording, encrypt and upload
if: always()
run: |
if [[ ! -f "/opt/screen_grab.mkv" ]];
then
exit 0;
fi
killall ffmpeg
sleep 5
killall -9 ffmpeg || true
sleep 5
# Feel free to add your own public key if you wish
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 6B3199AD9B3D23B8 # konstantin@cakewallet.com
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 35C8DBAFB8D9ACAC # cyjan@mrcyjanek.net
gpg --trust-model always --encrypt --output /opt/screen_grab.mkv.gpg \
--recipient 6B3199AD9B3D23B8 \
--recipient 35C8DBAFB8D9ACAC \
/opt/screen_grab.mkv
rm /opt/screen_grab.mkv
mv /opt/screen_grab.mkv.gpg ./screen_grab.mkv.gpg
- name: Upload Artifact to github
if: always()
continue-on-error: true
uses: actions/upload-artifact@v4
with:
path: ${{ github.workspace }}/screen_grab.mkv.gpg
name: tests_screen_grab

View file

@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_adaptive_back"/>
<foreground android:drawable="@mipmap/ic_launcher_adaptive_fore"/>
<monochrome android:drawable="@mipmap/ic_launcher_adaptive_mono"/>
</adaptive-icon>

Binary file not shown.

After

(image error) Size: 2.3 KiB

Binary file not shown.

After

(image error) Size: 1.5 KiB

Binary file not shown.

After

(image error) Size: 3.2 KiB

Binary file not shown.

After

(image error) Size: 5.1 KiB

Binary file not shown.

After

(image error) Size: 7.5 KiB

View file

@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_adaptive_back"/>
<foreground android:drawable="@mipmap/ic_launcher_adaptive_fore"/>
<monochrome android:drawable="@mipmap/ic_launcher_adaptive_mono"/>
</adaptive-icon>

Binary file not shown.

After

(image error) Size: 2.3 KiB

Binary file not shown.

After

(image error) Size: 1.5 KiB

Binary file not shown.

After

(image error) Size: 3 KiB

Binary file not shown.

After

(image error) Size: 4.8 KiB

Binary file not shown.

After

(image error) Size: 6.8 KiB

View file

@ -115,7 +115,7 @@ Install Flutter package dependencies with this command:
> `$ ./cakewallet.sh`
> and back to project root directory:
> `$ cd ../..`
> and fetch dependecies again
> and fetch dependencies again
> `$ flutter pub get`
Your CakeWallet binary will be built with some specific keys for iterate with 3rd party services. You may generate these secret keys placeholders with the following command:

View file

@ -16,14 +16,14 @@ These steps will help you configure and execute a build of CakeWallet from its s
### 1. Installing Package Dependencies
For build CakeWallet windows application from sources you will be needed to have:
> [Install Flutter]Follow installation guide (https://docs.flutter.dev/get-started/install/windows) and install do not miss to dev tools (install https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) which are required for windows desktop development (need to install Git for Windows and Visual Studio 2022). Then install `Desktop development with C++` packages via GUI Visual Studio 2022, or Visual Studio Build Tools 2022 including: `C++ Build Tools core features`, `C++ 2022 Redistributable Update`, `C++ core desktop features`, `MVC v143 - VS 2022 C++ x64/x86 build tools`, `C++ CMake tools for Windwos`, `Testing tools core features - Build Tools`, `C++ AddressSanitizer`.
> [Install Flutter]Follow installation guide (https://docs.flutter.dev/get-started/install/windows) and install do not miss to dev tools (install https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) which are required for windows desktop development (need to install Git for Windows and Visual Studio 2022). Then install `Desktop development with C++` packages via GUI Visual Studio 2022, or Visual Studio Build Tools 2022 including: `C++ Build Tools core features`, `C++ 2022 Redistributable Update`, `C++ core desktop features`, `MVC v143 - VS 2022 C++ x64/x86 build tools`, `C++ CMake tools for Windows`, `Testing tools core features - Build Tools`, `C++ AddressSanitizer`.
> [Install WSL] for building monero dependencies need to install Windows WSL (https://learn.microsoft.com/en-us/windows/wsl/install) and required packages for WSL (Ubuntu):
`$ sudo apt update `
`$ sudo apt build-essential cmake gcc-mingw-w64 g++-mingw-w64 autoconf libtool pkg-config`
### 2. Pull CakeWallet source code
You can downlaod CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git by following next command:
You can download CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git by following next command:
`$ git clone https://github.com/cake-tech/cake_wallet.git --branch MrCyjaneK-cyjan-monerodart`
OR you can download it as [Zip archive](https://github.com/cake-tech/cake_wallet/archive/refs/heads/MrCyjaneK-cyjan-monerodart.zip)

View file

@ -1,13 +1,18 @@
import 'dart:io';
import 'dart:math';
import 'package:cw_core/keyable.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:hive/hive.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:http/io_client.dart' as ioc;
import 'dart:math' as math;
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart' as crypto;
// import 'package:tor/tor.dart';
import 'package:crypto/crypto.dart';
part 'node.g.dart';
@ -170,34 +175,43 @@ class Node extends HiveObject with Keyable {
}
Future<bool> requestMoneroNode() async {
if (uri.toString().contains(".onion") || useSocksProxy) {
if (useSocksProxy) {
return await requestNodeWithProxy();
}
final path = '/json_rpc';
final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path);
final realm = 'monero-rpc';
final body = {'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'};
try {
final authenticatingClient = HttpClient();
authenticatingClient.badCertificateCallback =
((X509Certificate cert, String host, int port) => true);
authenticatingClient.addCredentials(
rpcUri,
realm,
HttpClientDigestCredentials(login ?? '', password ?? ''),
);
final http.Client client = ioc.IOClient(authenticatingClient);
final jsonBody = json.encode(body);
final response = await client.post(
rpcUri,
headers: {'Content-Type': 'application/json'},
body: json.encode(body),
body: jsonBody,
);
client.close();
// Check if we received a 401 Unauthorized response
if (response.statusCode == 401) {
final daemonRpc = DaemonRpc(
rpcUri.toString(),
username: login??'',
password: password??'',
);
final response = await daemonRpc.call('get_info', {});
return !(response['offline'] as bool);
}
printV("node check response: ${response.body}");
if ((response.body.contains("400 Bad Request") // Some other generic error
||
@ -225,7 +239,8 @@ class Node extends HiveObject with Keyable {
final resBody = json.decode(response.body) as Map<String, dynamic>;
return !(resBody['result']['offline'] as bool);
} catch (_) {
} catch (e) {
printV("error: $e");
return false;
}
}
@ -316,3 +331,150 @@ class Node extends HiveObject with Keyable {
}
}
}
/// https://github.com/ManyMath/digest_auth/
/// HTTP Digest authentication.
///
/// Adapted from https://github.com/dart-lang/http/issues/605#issue-963962341.
///
/// Created because http_auth was not working for Monero daemon RPC responses.
class DigestAuth {
final String username;
final String password;
String? realm;
String? nonce;
String? uri;
String? qop = "auth";
int _nonceCount = 0;
DigestAuth(this.username, this.password);
/// Initialize Digest parameters from the `WWW-Authenticate` header.
void initFromAuthorizationHeader(String authInfo) {
final Map<String, String>? values = _splitAuthenticateHeader(authInfo);
if (values != null) {
realm = values['realm'];
// Check if the nonce has changed.
if (nonce != values['nonce']) {
nonce = values['nonce'];
_nonceCount = 0; // Reset nonce count when nonce changes.
}
}
}
/// Generate the Digest Authorization header.
String getAuthString(String method, String uri) {
this.uri = uri;
_nonceCount++;
String cnonce = _computeCnonce();
String nc = _formatNonceCount(_nonceCount);
String ha1 = md5Hash("$username:$realm:$password");
String ha2 = md5Hash("$method:$uri");
String response = md5Hash("$ha1:$nonce:$nc:$cnonce:$qop:$ha2");
return 'Digest username="$username", realm="$realm", nonce="$nonce", uri="$uri", qop=$qop, nc=$nc, cnonce="$cnonce", response="$response"';
}
/// Helper to parse the `WWW-Authenticate` header.
Map<String, String>? _splitAuthenticateHeader(String? header) {
if (header == null || !header.startsWith('Digest ')) {
return null;
}
String token = header.substring(7); // Remove 'Digest '.
final Map<String, String> result = {};
final components = token.split(',').map((token) => token.trim());
for (final component in components) {
final kv = component.split('=');
final key = kv[0];
final value = kv.sublist(1).join('=').replaceAll('"', '');
result[key] = value;
}
return result;
}
/// Helper to compute a random cnonce.
String _computeCnonce() {
final math.Random rnd = math.Random();
final List<int> values = List<int>.generate(16, (i) => rnd.nextInt(256));
return hex.encode(values);
}
/// Helper to format the nonce count.
String _formatNonceCount(int count) =>
count.toRadixString(16).padLeft(8, '0');
/// Compute the MD5 hash of a string.
String md5Hash(String input) {
return md5.convert(utf8.encode(input)).toString();
}
}
class DaemonRpc {
final String rpcUrl;
final String username;
final String password;
DaemonRpc(this.rpcUrl, {required this.username, required this.password});
/// Perform a JSON-RPC call with Digest Authentication.
Future<Map<String, dynamic>> call(
String method, Map<String, dynamic> params) async {
final http.Client client = http.Client();
final DigestAuth digestAuth = DigestAuth(username, password);
// Initial request to get the `WWW-Authenticate` header.
final initialResponse = await client.post(
Uri.parse(rpcUrl),
headers: {
'Content-Type': 'application/json',
},
body: jsonEncode({
'jsonrpc': '2.0',
'id': '0',
'method': method,
'params': params,
}),
);
if (initialResponse.statusCode != 401 ||
!initialResponse.headers.containsKey('www-authenticate')) {
throw Exception('Unexpected response: ${initialResponse.body}');
}
// Extract Digest details from `WWW-Authenticate` header.
final String authInfo = initialResponse.headers['www-authenticate']!;
digestAuth.initFromAuthorizationHeader(authInfo);
// Create Authorization header for the second request.
String uri = Uri.parse(rpcUrl).path;
String authHeader = digestAuth.getAuthString('POST', uri);
// Make the authenticated request.
final authenticatedResponse = await client.post(
Uri.parse(rpcUrl),
headers: {
'Content-Type': 'application/json',
'Authorization': authHeader,
},
body: jsonEncode({
'jsonrpc': '2.0',
'id': '0',
'method': method,
'params': params,
}),
);
if (authenticatedResponse.statusCode != 200) {
throw Exception('RPC call failed: ${authenticatedResponse.body}');
}
final Map<String, dynamic> result = jsonDecode(authenticatedResponse.body) as Map<String, dynamic>;
if (result['error'] != null) {
throw Exception('RPC Error: ${result['error']}');
}
return result['result'] as Map<String, dynamic>;
}
}

View file

@ -9,6 +9,8 @@ import 'package:flutter/foundation.dart';
import 'package:monero/monero.dart' as monero;
import 'package:mutex/mutex.dart';
bool debugMonero = false;
int getSyncingHeight() {
// final height = monero.MONERO_cw_WalletListener_height(getWlptr());
final h2 = monero.Wallet_blockChainHeight(wptr!);
@ -132,7 +134,7 @@ Future<bool> setupNodeSync(
}
}
if (kDebugMode) {
if (kDebugMode && debugMonero) {
monero.Wallet_init3(
wptr!, argv0: '',
defaultLogBaseName: 'moneroc',

View file

@ -1,9 +1,7 @@
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_core/parseBoolFromString.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_monero/api/transaction_history.dart';
class MoneroTransactionInfo extends TransactionInfo {
MoneroTransactionInfo(this.txHash, this.height, this.direction, this.date,
@ -11,29 +9,6 @@ class MoneroTransactionInfo extends TransactionInfo {
this.confirmations) :
id = "${txHash}_${amount}_${accountIndex}_${addressIndex}";
MoneroTransactionInfo.fromMap(Map<String, Object?> map)
: id = "${map['hash']}_${map['amount']}_${map['accountIndex']}_${map['addressIndex']}",
txHash = map['hash'] as String,
height = (map['height'] ?? 0) as int,
direction = map['direction'] != null
? parseTransactionDirectionFromNumber(map['direction'] as String)
: TransactionDirection.incoming,
date = DateTime.fromMillisecondsSinceEpoch(
(int.tryParse(map['timestamp'] as String? ?? '') ?? 0) * 1000),
isPending = parseBoolFromString(map['isPending'] as String),
amount = map['amount'] as int,
accountIndex = int.parse(map['accountIndex'] as String),
addressIndex = map['addressIndex'] as int,
confirmations = map['confirmations'] as int,
key = getTxKey((map['hash'] ?? '') as String),
fee = map['fee'] as int? ?? 0 {
additionalInfo = <String, dynamic>{
'key': key,
'accountIndex': accountIndex,
'addressIndex': addressIndex
};
}
final String id;
final String txHash;
final int height;

View file

@ -265,6 +265,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
}
bool needExportOutputs(int amount) {
if (int.tryParse(monero.Wallet_secretSpendKey(wptr!)) != 0) {
return false;
}
// viewOnlyBalance - balance that we can spend
// TODO(mrcyjanek): remove hasUnknownKeyImages when we cleanup coin control
return (monero.Wallet_viewOnlyBalance(wptr!,

View file

@ -32,6 +32,11 @@ class CommonTestCases {
expect(textWidget, hasWidget ? findsOneWidget : findsNothing);
}
void hasTextAtLestOnce(String text, {bool hasWidget = true}) {
final textWidget = find.text(text);
expect(textWidget, hasWidget ? findsAny : findsNothing);
}
void hasType<T>() {
final typeWidget = find.byType(T);
expect(typeWidget, findsOneWidget);

View file

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/reactions/bip39_wallet_utils.dart';
import 'package:cake_wallet/wallet_types.g.dart';
@ -85,6 +87,7 @@ class CommonTestFlows {
await _confirmPreSeedInfo();
await _confirmWalletDetails();
await _commonTestCases.defaultSleepTime();
}
//* ========== Handles flow from welcome to restoring wallet from seeds ===============
@ -168,8 +171,8 @@ class CommonTestFlows {
await _walletListPageRobot.navigateToRestoreWalletOptionsPage();
await _commonTestCases.defaultSleepTime();
await _restoreOptionsPageRobot.navigateToRestoreFromSeedsOrKeysPage();
await _commonTestCases.defaultSleepTime();
if (!Platform.isLinux) await _restoreOptionsPageRobot.navigateToRestoreFromSeedsOrKeysPage();
if (!Platform.isLinux) await _commonTestCases.defaultSleepTime();
await _selectWalletTypeForWallet(walletType);
await _commonTestCases.defaultSleepTime();
@ -180,6 +183,7 @@ class CommonTestFlows {
//* ========== Handles setting up pin code for wallet on first install ===============
Future<void> setupPinCodeForWallet(List<int> pin) async {
if (Platform.isLinux) return;
// ----------- SetupPinCode Page -------------
// Confirm initial defaults - Widgets to be displayed etc
await _setupPinCodeRobot.isSetupPinCodePage();
@ -212,7 +216,7 @@ class CommonTestFlows {
await _welcomePageRobot.navigateToRestoreWalletPage();
await _restoreOptionsPageRobot.navigateToRestoreFromSeedsOrKeysPage();
if (!Platform.isLinux) await _restoreOptionsPageRobot.navigateToRestoreFromSeedsOrKeysPage();
await _selectWalletTypeForWallet(walletTypeToRestore);
}
@ -234,6 +238,12 @@ class CommonTestFlows {
await _newWalletPageRobot.generateWalletName();
if (Platform.isLinux) {
// manual pin input
await _restoreFromSeedOrKeysPageRobot.enterPasswordForWalletRestore(CommonTestConstants.pin.join(""));
await _restoreFromSeedOrKeysPageRobot.enterPasswordRepeatForWalletRestore(CommonTestConstants.pin.join(""));
}
await _newWalletPageRobot.onNextButtonPressed();
}
@ -252,11 +262,15 @@ class CommonTestFlows {
_walletSeedPageRobot.confirmWalletSeedReminderDisplays();
await _walletSeedPageRobot.onCopySeedsButtonPressed();
// await _walletSeedPageRobot.onCopySeedsButtonPressed();
await _walletSeedPageRobot.onNextButtonPressed();
await _walletSeedPageRobot.onConfirmButtonOnSeedAlertDialogPressed();
await _walletSeedPageRobot.onSeedPageVerifyButtonPressed();
// Turns out the popup about "Copied to clipboard" prevents
//the button from being pressed on the first try, by just
//tapping it again we fix it.
// await _walletSeedPageRobot.onSeedPageVerifyButtonPressed();
await _walletSeedPageRobot.onOpenWalletButtonPressed();
}
//* Main Restore Actions - On the RestoreFromSeed/Keys Page - Restore from Seeds Action
@ -277,6 +291,12 @@ class CommonTestFlows {
.enterBlockHeightForWalletRestore(secrets.moneroTestWalletBlockHeight);
}
if (Platform.isLinux) {
// manual pin input
await _restoreFromSeedOrKeysPageRobot.enterPasswordForWalletRestore(CommonTestConstants.pin.join(""));
await _restoreFromSeedOrKeysPageRobot.enterPasswordRepeatForWalletRestore(CommonTestConstants.pin.join(""));
}
await _restoreFromSeedOrKeysPageRobot.onRestoreWalletButtonPressed();
}

View file

@ -67,6 +67,11 @@ void main() {
await authPageRobot.enterPinCode(CommonTestConstants.pin);
}
final onAuthPageDesktop = authPageRobot.onAuthPageDesktop();
if (onAuthPageDesktop) {
await authPageRobot.enterPassword(CommonTestConstants.pin.join(""));
}
// ----------- Exchange Confirm Page -------------
await exchangeConfirmPageRobot.isExchangeConfirmPage();

View file

@ -20,6 +20,11 @@ class AuthPageRobot extends PinCodeWidgetRobot {
return hasPin;
}
bool onAuthPageDesktop() {
final hasWalletPasswordInput = find.byKey(ValueKey('enter_wallet_password'));
return hasWalletPasswordInput.tryEvaluate();
}
Future<void> isAuthPage() async {
await commonTestCases.isSpecificPage<AuthPage>();
}

View file

@ -24,6 +24,20 @@ class PinCodeWidgetRobot {
commonTestCases.hasValueKey('pin_code_button_0_key');
}
Future<void> enterPassword(String password, {int pumpDuration = 100}) async {
await commonTestCases.enterText(
password,
'enter_wallet_password',
);
await tester.pumpAndSettle();
await commonTestCases.tapItemByKey(
'unlock',
);
await tester.pumpAndSettle();
await commonTestCases.defaultSleepTime();
}
Future<void> enterPinCode(List<int> pinCode, {int pumpDuration = 100}) async {
for (int pin in pinCode) {
await commonTestCases.tapItemByKey(

View file

@ -1,7 +1,9 @@
import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/widgets/seed_widget.dart';
import 'package:cake_wallet/src/widgets/validable_annotated_editable_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../components/common_test_cases.dart';
@ -65,12 +67,28 @@ class RestoreFromSeedOrKeysPageRobot {
Future<void> enterSeedPhraseForWalletRestore(String text) async {
ValidatableAnnotatedEditableTextState seedTextState =
await tester.state(find.byType(ValidatableAnnotatedEditableText));
await tester.state(find.byType(ValidatableAnnotatedEditableText));
seedTextState.widget.controller.text = text;
await tester.pumpAndSettle();
}
Future<void> enterPasswordForWalletRestore(String text) async {
await commonTestCases.enterText(
text,
'password',
);
await tester.pumpAndSettle();
}
Future<void> enterPasswordRepeatForWalletRestore(String text) async {
await commonTestCases.enterText(
text,
'repeat_wallet_password',
);
await tester.pumpAndSettle();
}
Future<void> enterBlockHeightForWalletRestore(String blockHeight) async {
await commonTestCases.enterText(
blockHeight,

View file

@ -183,32 +183,15 @@ class SendPageRobot {
}
Future<void> _handleAuthPage() async {
tester.printToConsole('Inside _handleAuth');
await tester.pump();
tester.printToConsole('starting auth checks');
final authPage = authPageRobot.onAuthPage();
tester.printToConsole('hasAuth:$authPage');
if (authPage) {
await tester.pump();
tester.printToConsole('Starting inner _handleAuth loop checks');
try {
await authPageRobot.enterPinCode(CommonTestConstants.pin, pumpDuration: 500);
tester.printToConsole('Auth done');
await tester.pump();
tester.printToConsole('Auth pump done');
} catch (e) {
tester.printToConsole('Auth failed, retrying');
await tester.pump();
_handleAuthPage();
}
final onAuthPage = authPageRobot.onAuthPage();
if (onAuthPage) {
await authPageRobot.enterPinCode(CommonTestConstants.pin);
}
final onAuthPageDesktop = authPageRobot.onAuthPageDesktop();
if (onAuthPageDesktop) {
await authPageRobot.enterPassword(CommonTestConstants.pin.join(""));
}
await tester.pump();
}
Future<void> handleSendResult() async {
@ -221,7 +204,7 @@ class SendPageRobot {
tester.printToConsole('Has an Error in the handle: $hasError');
int maxRetries = 20;
int maxRetries = 3;
int retries = 0;
while (hasError && retries < maxRetries) {

View file

@ -14,8 +14,13 @@ class WalletSeedPageRobot {
await commonTestCases.isSpecificPage<WalletSeedPage>();
}
Future<void> onNextButtonPressed() async {
await commonTestCases.tapItemByKey('wallet_seed_page_next_button_key');
Future<void> onSeedPageVerifyButtonPressed() async {
await commonTestCases.tapItemByKey('wallet_seed_page_verify_seed_button_key');
await commonTestCases.defaultSleepTime();
}
Future<void> onOpenWalletButtonPressed() async {
await commonTestCases.tapItemByKey('wallet_seed_page_open_wallet_button_key');
await commonTestCases.defaultSleepTime();
}
@ -38,11 +43,14 @@ class WalletSeedPageRobot {
final walletSeeds = walletSeedViewModel.seed;
commonTestCases.hasText(walletName);
commonTestCases.hasText(walletSeeds);
final seedList = walletSeeds.trim().split(" ");
for (final seedWord in seedList) {
commonTestCases.hasTextAtLestOnce(seedWord);
}
}
void confirmWalletSeedReminderDisplays() {
commonTestCases.hasText(S.current.seed_reminder);
commonTestCases.hasText(S.current.cake_seeds_save_disclaimer);
}
Future<void> onSaveSeedsButtonPressed() async {

View file

@ -1,9 +1,13 @@
import 'dart:io';
import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../components/common_test_cases.dart';
import '../components/common_test_constants.dart';
import '../components/common_test_flows.dart';
import '../robots/auth_page_robot.dart';
@ -95,6 +99,10 @@ Future<void> _confirmSeedsFlowForWalletType(
await authPageRobot.enterPinCode(CommonTestConstants.pin);
}
final onAuthPageDesktop = authPageRobot.onAuthPageDesktop();
if (onAuthPageDesktop) {
await authPageRobot.enterPassword(CommonTestConstants.pin.join(""));
}
await tester.pumpAndSettle();
await walletKeysAndSeedPageRobot.isWalletKeysAndSeedPage();

View file

@ -56,6 +56,10 @@ void main() {
await authPageRobot.enterPinCode(CommonTestConstants.pin);
}
final onAuthPageDesktop = authPageRobot.onAuthPageDesktop();
if (onAuthPageDesktop) {
await authPageRobot.enterPassword(CommonTestConstants.pin.join(""));
}
await exchangeConfirmPageRobot.onSavedTradeIdButtonPressed();
await exchangeTradePageRobot.onGotItButtonPressed();
});

View file

@ -1,4 +1,7 @@
import 'dart:io';
import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

View file

@ -1,4 +1,5 @@
#!/bin/bash
export DESKTOP_FORCE_MOBILE="Y"
declare -a targets
declare -a passed_tests
@ -12,6 +13,10 @@ done < <(find integration_test/test_suites -name "*.dart" -type f -print0)
# Run each test and collect results
for target in "${targets[@]}"
do
if [[ "x$REMOVE_DATA_DIRECTORY" == "xY" ]];
then
rm -rf ~/.local/share/com.example.cake_wallet ~/Documents/cake_wallet
fi
echo "Running test: $target"
if flutter drive \
--driver=test_driver/integration_test.dart \

View file

@ -293,6 +293,7 @@ class BackupService {
final lookupsUnstoppableDomains = data[PreferencesKey.lookupsUnstoppableDomains] as bool?;
final lookupsOpenAlias = data[PreferencesKey.lookupsOpenAlias] as bool?;
final lookupsENS = data[PreferencesKey.lookupsENS] as bool?;
final lookupsWellKnown = data[PreferencesKey.lookupsWellKnown] as bool?;
final syncAll = data[PreferencesKey.syncAllKey] as bool?;
final syncMode = data[PreferencesKey.syncModeKey] as int?;
final autoGenerateSubaddressStatus =
@ -403,6 +404,9 @@ class BackupService {
if (lookupsENS != null) await _sharedPreferences.setBool(PreferencesKey.lookupsENS, lookupsENS);
if (lookupsWellKnown != null)
await _sharedPreferences.setBool(PreferencesKey.lookupsWellKnown, lookupsWellKnown);
if (syncAll != null) await _sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll);
if (syncMode != null) await _sharedPreferences.setInt(PreferencesKey.syncModeKey, syncMode);
@ -542,6 +546,8 @@ class BackupService {
_sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains),
PreferencesKey.lookupsOpenAlias: _sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias),
PreferencesKey.lookupsENS: _sharedPreferences.getBool(PreferencesKey.lookupsENS),
PreferencesKey.lookupsWellKnown:
_sharedPreferences.getBool(PreferencesKey.lookupsWellKnown),
PreferencesKey.syncModeKey: _sharedPreferences.getInt(PreferencesKey.syncModeKey),
PreferencesKey.syncAllKey: _sharedPreferences.getBool(PreferencesKey.syncAllKey),
PreferencesKey.autoGenerateSubaddressStatusKey:

View file

@ -334,20 +334,20 @@ Future<void> defaultSettingsMigration(
);
break;
case 46:
_fixNodesUseSSLFlag(nodes);
updateWalletTypeNodesWithNewNode(
await _fixNodesUseSSLFlag(nodes);
await updateWalletTypeNodesWithNewNode(
newNodeUri: 'litecoin.stackwallet.com:20063',
nodes: nodes,
type: WalletType.litecoin,
useSSL: true,
);
updateWalletTypeNodesWithNewNode(
await updateWalletTypeNodesWithNewNode(
newNodeUri: 'electrum-ltc.bysh.me:50002',
nodes: nodes,
type: WalletType.litecoin,
useSSL: true,
);
_changeDefaultNode(
await _changeDefaultNode(
nodes: nodes,
sharedPreferences: sharedPreferences,
type: WalletType.solana,
@ -360,13 +360,13 @@ Future<void> defaultSettingsMigration(
'solana-rpc.publicnode.com:443',
],
);
_updateNode(
await _updateNode(
nodes: nodes,
currentUri: "ethereum.publicnode.com",
newUri: "ethereum-rpc.publicnode.com",
useSSL: true,
);
_updateNode(
await _updateNode(
nodes: nodes,
currentUri: "polygon-bor.publicnode.com",
newUri: "polygon-bor-rpc.publicnode.com",
@ -387,12 +387,12 @@ Future<void> defaultSettingsMigration(
await sharedPreferences.setInt(PreferencesKey.currentDefaultSettingsMigrationVersion, version);
}
void _updateNode({
Future<void> _updateNode({
required Box<Node> nodes,
required String currentUri,
String? newUri,
bool? useSSL,
}) {
}) async {
for (Node node in nodes.values) {
if (node.uriRaw == currentUri) {
if (newUri != null) {
@ -401,6 +401,7 @@ void _updateNode({
if (useSSL != null) {
node.useSSL = useSSL;
}
await node.save();
}
}
}
@ -481,7 +482,7 @@ void _deselectExchangeProvider(SharedPreferences sharedPreferences, String provi
);
}
void _fixNodesUseSSLFlag(Box<Node> nodes) {
Future<void> _fixNodesUseSSLFlag(Box<Node> nodes) async {
for (Node node in nodes.values) {
switch (node.uriRaw) {
case cakeWalletLitecoinElectrumUri:
@ -490,6 +491,7 @@ void _fixNodesUseSSLFlag(Box<Node> nodes) {
case newCakeWalletMoneroUri:
node.useSSL = true;
node.trusted = true;
await node.save();
}
}
}

View file

@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/openalias_record.dart';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
import 'package:cake_wallet/entities/emoji_string_extension.dart';
import 'package:cake_wallet/entities/wellknown_record.dart';
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
import 'package:cake_wallet/mastodon/mastodon_api.dart';
import 'package:cake_wallet/nostr/nostr_api.dart';
@ -208,6 +209,17 @@ class AddressResolver {
}
}
// .well-known scheme:
if (text.contains('.') && text.contains('@')) {
if (settingsStore.lookupsWellKnown) {
final record =
await WellKnownRecord.fetchAddressAndName(formattedName: text, currency: currency);
if (record != null) {
return ParsedAddress.fetchWellKnownAddress(address: record.address, name: text);
}
}
}
if (!text.startsWith('@') && text.contains('@') && !text.contains('.')) {
final bool isFioRegistered = await FioAddressProvider.checkAvail(text);
if (isFioRegistered) {

View file

@ -12,7 +12,8 @@ enum ParseFrom {
contact,
mastodon,
nostr,
thorChain
thorChain,
wellKnown
}
class ParsedAddress {
@ -142,6 +143,14 @@ class ParsedAddress {
);
}
factory ParsedAddress.fetchWellKnownAddress({required String address, required String name}) {
return ParsedAddress(
addresses: [address],
name: name,
parseFrom: ParseFrom.wellKnown,
);
}
final List<String> addresses;
final String name;
final String description;

View file

@ -76,6 +76,7 @@ class PreferencesKey {
static const lookupsUnstoppableDomains = 'looks_up_unstoppable_domain';
static const lookupsOpenAlias = 'looks_up_open_alias';
static const lookupsENS = 'looks_up_ens';
static const lookupsWellKnown = 'looks_up_well_known';
static const showCameraConsent = 'show_camera_consent';
static String moneroWalletUpdateV1Key(String name) =>

View file

@ -0,0 +1,92 @@
import 'dart:convert';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:http/http.dart' as http;
class WellKnownRecord {
WellKnownRecord({
required this.address,
required this.name,
});
final String name;
final String address;
static Future<String?> checkWellKnownUsername(String username, CryptoCurrency currency) async {
String jsonLocation = "";
switch (currency) {
case CryptoCurrency.nano:
jsonLocation = "nano-currency";
break;
// TODO: add other currencies
default:
return null;
}
// split the string by the @ symbol:
try {
final List<String> splitStrs = username.split("@");
String name = splitStrs.first.toLowerCase();
final String domain = splitStrs.last;
if (splitStrs.length == 3) {
// for username like @alice@domain.org instead of alice@domain.org
name = splitStrs[1];
}
if (name.isEmpty) {
name = "_";
}
// lookup domain/.well-known/nano-currency.json and check if it has a nano address:
final http.Response response = await http.get(
Uri.parse("https://$domain/.well-known/$jsonLocation.json?names=$name"),
headers: <String, String>{"Accept": "application/json"},
);
if (response.statusCode != 200) {
return null;
}
final Map<String, dynamic> decoded = json.decode(response.body) as Map<String, dynamic>;
// Access the first element in the names array and retrieve its address
final List<dynamic> names = decoded["names"] as List<dynamic>;
for (final dynamic item in names) {
if (item["name"].toLowerCase() == name) {
return item["address"] as String;
}
}
} catch (e) {
printV("error checking well-known username: $e");
}
return null;
}
static String formatDomainName(String name) {
String formattedName = name;
if (name.contains("@")) {
formattedName = name.replaceAll("@", ".");
}
return formattedName;
}
static Future<WellKnownRecord?> fetchAddressAndName({
required String formattedName,
required CryptoCurrency currency,
}) async {
String name = formattedName;
printV("formattedName: $formattedName");
final address = await checkWellKnownUsername(formattedName, currency);
if (address == null) {
return null;
}
return WellKnownRecord(address: address, name: name);
}
}

View file

@ -50,6 +50,7 @@ import 'package:cake_wallet/src/screens/new_wallet/advanced_privacy_settings_pag
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/wallet_group_description_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/wallet_group_existing_seed_description_page.dart';
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
@ -587,6 +588,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
return MaterialPageRoute<void>(
builder: (_) => getIt.get<PreSeedPage>(param1: settings.arguments as int));
case Routes.walletGroupExistingSeedDescriptionPage:
return MaterialPageRoute<void>(
builder: (_) => WalletGroupExistingSeedDescriptionPage(
seedPhraseWordsLength: settings.arguments as int));
case Routes.transactionSuccessPage:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<TransactionSuccessPage>(param1: settings.arguments as String));

View file

@ -116,5 +116,6 @@ class Routes {
static const urqrAnimatedPage = '/urqr/animated_page';
static const walletGroupsDisplayPage = '/wallet_groups_display_page';
static const walletGroupDescription = '/wallet_group_description';
static const walletGroupExistingSeedDescriptionPage = '/wallet_group_existing_seed_description_page';
static const walletSeedVerificationPage = '/wallet_seed_verification_page';
}

View file

@ -231,6 +231,8 @@ class ExchangePage extends BasePage {
key: ValueKey('exchange_page_exchange_button_key'),
text: S.of(context).exchange,
onPressed: () {
FocusScope.of(context).unfocus();
if (_formKey.currentState != null &&
_formKey.currentState!.validate()) {
if ((exchangeViewModel.depositCurrency == CryptoCurrency.xmr) &&

View file

@ -101,8 +101,14 @@ class _WalletNameFormState extends State<WalletNameForm> {
void initState() {
_stateReaction ??= reaction((_) => _walletNewVM.state, (ExecutionState state) async {
if (state is ExecutedSuccessfullyState) {
Navigator.of(navigatorKey.currentContext ?? context)
.pushNamed(Routes.preSeedPage, arguments: _walletNewVM.seedPhraseWordsLength);
if (widget.isChildWallet) {
Navigator.of(navigatorKey.currentContext ?? context)
.pushNamed(Routes.walletGroupExistingSeedDescriptionPage,
arguments: _walletNewVM.seedPhraseWordsLength);
} else {
Navigator.of(navigatorKey.currentContext ?? context)
.pushNamed(Routes.preSeedPage, arguments: _walletNewVM.seedPhraseWordsLength);
}
}
if (state is FailureState) {
@ -221,6 +227,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
),
if (_walletNewVM.hasWalletPassword) ...[
TextFormField(
key: ValueKey('password'),
onChanged: (value) => _walletNewVM.walletPassword = value,
controller: _passwordController,
textAlign: TextAlign.center,
@ -257,6 +264,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
),
),
TextFormField(
key: ValueKey('repeat_wallet_password'),
onChanged: (value) => _walletNewVM.repeatedWalletPassword = value,
controller: _repeatedPasswordController,
textAlign: TextAlign.center,

View file

@ -7,6 +7,7 @@ import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/themes/extensions/theme_type_images.dart';
class WalletGroupDescriptionPage extends BasePage {
WalletGroupDescriptionPage({required this.selectedWalletType});
@ -25,10 +26,7 @@ class WalletGroupDescriptionPage extends BasePage {
padding: EdgeInsets.all(24),
child: Column(
children: [
Image.asset(
_getThemedWalletGroupImage(currentTheme.type),
height: 200,
),
Image.asset(currentTheme.type.walletGroupImage, height: 200),
SizedBox(height: 32),
Expanded(
child: Text.rich(
@ -91,19 +89,4 @@ class WalletGroupDescriptionPage extends BasePage {
),
);
}
String _getThemedWalletGroupImage(ThemeType theme) {
final lightImage = 'assets/images/wallet_group_light.png';
final darkImage = 'assets/images/wallet_group_dark.png';
final brightImage = 'assets/images/wallet_group_bright.png';
switch (theme) {
case ThemeType.bright:
return brightImage;
case ThemeType.light:
return lightImage;
default:
return darkImage;
}
}
}

View file

@ -4,12 +4,13 @@ import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/theme_type_images.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/view_model/wallet_groups_display_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import '../../../themes/extensions/cake_text_theme.dart';
class WalletGroupsDisplayPage extends BasePage {
WalletGroupsDisplayPage(this.walletGroupsDisplayViewModel);
@ -165,10 +166,7 @@ class WalletGroupEmptyStateWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
Image.asset(
_getThemedWalletGroupImage(currentTheme.type),
scale: 1.8,
),
Image.asset(currentTheme.type.walletGroupImage, scale: 1.8),
SizedBox(height: 32),
Text.rich(
TextSpan(
@ -194,19 +192,4 @@ class WalletGroupEmptyStateWidget extends StatelessWidget {
],
);
}
String _getThemedWalletGroupImage(ThemeType theme) {
final lightImage = 'assets/images/wallet_group_light.png';
final darkImage = 'assets/images/wallet_group_dark.png';
final brightImage = 'assets/images/wallet_group_bright.png';
switch (theme) {
case ThemeType.bright:
return brightImage;
case ThemeType.light:
return lightImage;
default:
return darkImage;
}
}
}

View file

@ -0,0 +1,106 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/themes/extensions/theme_type_images.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/material.dart';
class WalletGroupExistingSeedDescriptionPage extends BasePage {
WalletGroupExistingSeedDescriptionPage({required this.seedPhraseWordsLength});
final int seedPhraseWordsLength;
@override
String get title => S.current.wallet_group;
@override
Widget body(BuildContext context) {
final textStyle = TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
);
return Container(
alignment: Alignment.center,
padding: EdgeInsets.all(24),
child: Column(
children: [
Image.asset(currentTheme.type.walletGroupImage, height: 200),
SizedBox(height: 32),
Expanded(
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: S.current.wallet_group_description_existing_seed + '\n\n',
style: textStyle),
TextSpan(
text: S.current.wallet_group_description_open_wallet + '\n\n',
style: textStyle),
TextSpan(
text: S.current.wallet_group_description_view_seed + '\n', style: textStyle),
TextSpan(
text: S.current.seed_display_path,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w800,
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
),
),
],
),
textAlign: TextAlign.center,
),
),
Column(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: PrimaryButton(
key: ValueKey(
'wallet_group_existing_seed_description_page_verify_seed_button_key'),
onPressed: () => Navigator.pushNamed(context, Routes.preSeedPage,
arguments: seedPhraseWordsLength),
text: S.current.verify_seed,
color: Theme.of(context).cardColor,
textColor: currentTheme.type == ThemeType.dark
? Theme.of(context).extension<DashboardPageTheme>()!.textColor
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor,
),
),
),
Flexible(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Builder(
builder: (context) => PrimaryButton(
key: ValueKey(
'wallet_group_existing_seed_description_page_open_wallet_button_key'),
onPressed: () {
Navigator.of(context).popUntil((route) => route.isFirst);
},
text: S.current.open_wallet,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
),
),
)
],
),
SizedBox(height: 12),
],
)
],
),
);
}
}

View file

@ -148,12 +148,14 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
),
if (widget.displayWalletPassword)
...[Container(
key: ValueKey('password'),
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: passwordTextEditingController,
hintText: S.of(context).password,
obscureText: true)),
Container(
key: ValueKey('repeat_wallet_password'),
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: repeatedPasswordTextEditingController,

View file

@ -223,12 +223,14 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
),
if (widget.displayWalletPassword)
...[BaseTextFormField(
key: ValueKey('password'),
controller: passwordTextEditingController,
hintText: S
.of(context)
.password,
obscureText: true),
BaseTextFormField(
key: ValueKey('repeat_wallet_password'),
controller: repeatedPasswordTextEditingController,
hintText: S
.of(context)

View file

@ -525,6 +525,10 @@ class SendPage extends BasePage {
if (state is TransactionCommitted) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (!context.mounted) {
return;
}
final successMessage = S.of(context).send_success(
sendViewModel.selectedCryptoCurrency.toString());

View file

@ -30,6 +30,11 @@ Future<String> extractAddressFromParsed(
content = S.of(context).extracted_address_content('${parsedAddress.name} (OpenAlias)');
address = parsedAddress.addresses.first;
break;
case ParseFrom.wellKnown:
title = S.of(context).address_detected;
content = S.of(context).extracted_address_content('${parsedAddress.name} (Well-Known)');
address = parsedAddress.addresses.first;
break;
case ParseFrom.fio:
title = S.of(context).address_detected;
content = S.of(context).extracted_address_content('${parsedAddress.name} (FIO)');

View file

@ -45,6 +45,10 @@ class DomainLookupsPage extends BasePage {
title: 'Ethereum Name Service',
value: _privacySettingsViewModel.looksUpENS,
onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsENS(value)),
SettingsSwitcherCell(
title: '.well-known',
value: _privacySettingsViewModel.looksUpWellKnown,
onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsWellKnown(value)),
//if (!isHaven) it does not work correctly
],

View file

@ -170,6 +170,7 @@ class WalletUnlockPageState extends AuthPageState<WalletUnlockPage> {
SizedBox(height: 24),
Form(
child: TextFormField(
key: ValueKey('enter_wallet_password'),
onChanged: (value) => null,
controller: _passwordController,
textAlign: TextAlign.center,
@ -205,6 +206,7 @@ class WalletUnlockPageState extends AuthPageState<WalletUnlockPage> {
),
),
Padding(
key: ValueKey('unlock'),
padding: EdgeInsets.only(bottom: 24),
child: Observer(
builder: (_) => LoadingPrimaryButton(

View file

@ -91,7 +91,10 @@ class LoadingPrimaryButton extends StatelessWidget {
width: double.infinity,
height: 52.0,
child: TextButton(
onPressed: (isLoading || isDisabled) ? null : onPressed,
onPressed: (isLoading || isDisabled) ? null : () {
FocusScope.of(context).unfocus();
onPressed.call();
},
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(isDisabled ? color.withOpacity(0.5) : color),

View file

@ -115,6 +115,7 @@ abstract class SettingsStoreBase with Store {
required this.lookupsUnstoppableDomains,
required this.lookupsOpenAlias,
required this.lookupsENS,
required this.lookupsWellKnown,
required this.customBitcoinFeeRate,
required this.silentPaymentsCardDisplay,
required this.silentPaymentsAlwaysScan,
@ -459,6 +460,11 @@ abstract class SettingsStoreBase with Store {
reaction((_) => lookupsENS,
(bool looksUpENS) => _sharedPreferences.setBool(PreferencesKey.lookupsENS, looksUpENS));
reaction(
(_) => lookupsWellKnown,
(bool looksUpWellKnown) =>
_sharedPreferences.setBool(PreferencesKey.lookupsWellKnown, looksUpWellKnown));
// secure storage keys:
reaction(
(_) => allowBiometricalAuthentication,
@ -772,6 +778,8 @@ abstract class SettingsStoreBase with Store {
@observable
bool lookupsENS;
@observable
bool lookupsWellKnown;
@observable
SyncMode currentSyncMode;
@ -967,6 +975,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains) ?? true;
final lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true;
final lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true;
final lookupsWellKnown = sharedPreferences.getBool(PreferencesKey.lookupsWellKnown) ?? true;
final customBitcoinFeeRate = sharedPreferences.getInt(PreferencesKey.customBitcoinFeeRate) ?? 1;
final silentPaymentsCardDisplay =
sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true;
@ -1245,6 +1254,7 @@ abstract class SettingsStoreBase with Store {
lookupsUnstoppableDomains: lookupsUnstoppableDomains,
lookupsOpenAlias: lookupsOpenAlias,
lookupsENS: lookupsENS,
lookupsWellKnown: lookupsWellKnown,
customBitcoinFeeRate: customBitcoinFeeRate,
silentPaymentsCardDisplay: silentPaymentsCardDisplay,
silentPaymentsAlwaysScan: silentPaymentsAlwaysScan,
@ -1414,6 +1424,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains) ?? true;
lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true;
lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true;
lookupsWellKnown = sharedPreferences.getBool(PreferencesKey.lookupsWellKnown) ?? true;
customBitcoinFeeRate = sharedPreferences.getInt(PreferencesKey.customBitcoinFeeRate) ?? 1;
silentPaymentsCardDisplay =
sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true;

View file

@ -0,0 +1,14 @@
import 'package:cake_wallet/themes/theme_base.dart';
extension ThemeTypeImages on ThemeType {
String get walletGroupImage {
switch (this) {
case ThemeType.bright:
return 'assets/images/wallet_group_bright.png';
case ThemeType.light:
return 'assets/images/wallet_group_light.png';
default:
return 'assets/images/wallet_group_dark.png';
}
}
}

View file

@ -94,6 +94,9 @@ abstract class PrivacySettingsViewModelBase with Store {
@computed
bool get looksUpENS => _settingsStore.lookupsENS;
@computed
bool get looksUpWellKnown => _settingsStore.lookupsWellKnown;
bool get canUseEtherscan => _wallet.type == WalletType.ethereum;
bool get canUsePolygonScan => _wallet.type == WalletType.polygon;
@ -130,6 +133,9 @@ abstract class PrivacySettingsViewModelBase with Store {
@action
void setLookupsENS(bool value) => _settingsStore.lookupsENS = value;
@action
void setLookupsWellKnown(bool value) => _settingsStore.lookupsWellKnown = value;
@action
void setLookupsYatService(bool value) => _settingsStore.lookupsYatService = value;

View file

@ -46,8 +46,11 @@ static void my_application_activate(GApplication* application) {
} else {
gtk_window_set_title(window, "Cake Wallet");
}
gtk_window_set_default_size(window, 1280, 720);
if (getenv("DESKTOP_FORCE_MOBILE")) {
gtk_window_set_default_size(window, 720, 1280);
} else {
gtk_window_set_default_size(window, 1280, 720);
}
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();

View file

@ -1,18 +1,24 @@
#!/bin/bash
set -x -e
cd cw_core; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
cd cw_evm; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
cd cw_monero; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
cd cw_bitcoin; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
cd cw_haven; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
cd cw_nano; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
cd cw_bitcoin_cash; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
cd cw_solana; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
cd cw_tron; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
cd cw_wownero; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
cd cw_polygon; flutter pub get; cd ..
cd cw_ethereum; flutter pub get; cd ..
cd cw_mweb && flutter pub get && cd ..
dart run build_runner build --delete-conflicting-outputs
for cwcoin in cw_{core,evm,monero,bitcoin,haven,nano,bitcoin_cash,solana,tron,wownero}
do
if [[ "x$1" == "xasync" ]];
then
bash -c "cd $cwcoin; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .." &
else
bash -c "cd $cwcoin; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .."
fi
done
for cwcoin in cw_{polygon,ethereum,mwebd};
do
if [[ "x$1" == "xasync" ]];
then
bash -c "cd $cwcoin; flutter pub get; cd .." &
else
bash -c "cd $cwcoin; flutter pub get; cd .."
fi
done
flutter pub get
dart run build_runner build --delete-conflicting-outputs

View file

@ -107,7 +107,10 @@ dependencies:
polyseed: ^0.0.6
nostr_tools: ^1.0.9
solana: ^0.31.0+1
ledger_flutter_plus: ^1.4.1
ledger_flutter_plus:
git:
url: https://github.com/vespr-wallet/ledger-flutter-plus
ref: c2e341d8038f1108690ad6f80f7b4b7156aacc76
hashlib: ^1.19.2
dev_dependencies:
@ -146,6 +149,10 @@ dependency_overrides:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v9
ffi: 2.1.0
ledger_flutter_plus:
git:
url: https://github.com/vespr-wallet/ledger-flutter-plus
ref: c2e341d8038f1108690ad6f80f7b4b7156aacc76
flutter_icons:
image_path: "assets/images/app_logo.png"

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "ﺕﻼﻣﺎﻌﻤﻟﺍ ﻞﺠﺳ ﻲﻓ ﺔﻠﻣﺎﻌﻤﻟﺍ ﺲﻜﻌﻨﺗ ﻰﺘﺣ ﻥﺍﻮﺛ ﻊﻀﺒﻟ ﺭﺎﻈﺘﻧﻻﺍ ﻰﺟﺮﻳ",
"wallet": "محفظة",
"wallet_group": "مجموعة محفظة",
"wallet_group_description_existing_seed": "لقد اخترت استخدام بذرة موجودة لهذه المحفظة. يمكنك التحقق من البذرة مرة أخرى إذا كنت بحاجة إلى تأكيدها أو كتابتها.",
"wallet_group_description_four": "لإنشاء محفظة مع بذرة جديدة تماما.",
"wallet_group_description_one": "في محفظة الكيك ، يمكنك إنشاء ملف",
"wallet_group_description_open_wallet": "خلاف ذلك ، يمكنك الاستمرار في فتح المحفظة",
"wallet_group_description_three": "لرؤية المحافظ المتاحة و/أو شاشة مجموعات المحفظة. أو اختر",
"wallet_group_description_two": "عن طريق اختيار محفظة موجودة لتبادل البذور مع. يمكن أن تحتوي كل مجموعة محفظة على محفظة واحدة من كل نوع من العملة. \n\n يمكنك تحديدها",
"wallet_group_description_view_seed": "يمكنك دائمًا عرض هذه البذرة مرة أخرى تحت",
"wallet_group_empty_state_text_one": "يبدو أنه ليس لديك أي مجموعات محفظة متوافقة !\n\n انقر",
"wallet_group_empty_state_text_two": "أدناه لجعل واحدة جديدة.",
"wallet_keys": "سييد المحفظة / المفاتيح",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "Моля, изчакайте няколко секунди, докато транзакцията се отрази в историята на транзакциите",
"wallet": "Портфейл",
"wallet_group": "Група на портфейла",
"wallet_group_description_existing_seed": "Вие сте избрали да използвате съществуващо семе за този портфейл. Можете да проверите отново семето, ако трябва да го потвърдите или запишете.",
"wallet_group_description_four": "За да създадете портфейл с изцяло ново семе.",
"wallet_group_description_one": "В портфейла за торта можете да създадете a",
"wallet_group_description_open_wallet": "В противен случай можете да продължите да отваряте портфейла",
"wallet_group_description_three": "За да видите наличния екран за портфейли и/или групи за портфейли. Или изберете",
"wallet_group_description_two": "Чрез избора на съществуващ портфейл, с който да споделите семе. Всяка група за портфейл може да съдържа по един портфейл от всеки тип валута. \n\n Можете да изберете",
"wallet_group_description_view_seed": "Винаги можете да видите това семе отново под",
"wallet_group_empty_state_text_one": "Изглежда, че нямате съвместими групи портфейли !\n\n tap",
"wallet_group_empty_state_text_two": "по -долу, за да се направи нов.",
"wallet_keys": "Seed/keys на портфейла",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "Počkejte několik sekund, než se transakce projeví v historii transakcí",
"wallet": "Peněženka",
"wallet_group": "Skupina peněženky",
"wallet_group_description_existing_seed": "Rozhodli jste se použít existující semeno pro tuto peněženku. Semeno můžete znovu ověřit, pokud potřebujete potvrdit nebo zapisovat.",
"wallet_group_description_four": "Vytvoření peněženky s zcela novým semenem.",
"wallet_group_description_one": "V peněžence dortu můžete vytvořit a",
"wallet_group_description_open_wallet": "Jinak můžete pokračovat v otevírání peněženky",
"wallet_group_description_three": "Chcete -li zobrazit dostupnou obrazovku Skupina skupin peněženek a/nebo skupin peněženek. Nebo zvolit",
"wallet_group_description_two": "Výběrem existující peněženky pro sdílení semeno. Každá skupina peněženek může obsahovat jednu peněženku každého typu měny. \n\n Můžete si vybrat",
"wallet_group_description_view_seed": "Toto semeno si můžete vždy znovu prohlédnout",
"wallet_group_empty_state_text_one": "Vypadá to, že nemáte žádné kompatibilní skupiny peněženky !\n\n",
"wallet_group_empty_state_text_two": "Níže vytvořit nový.",
"wallet_keys": "Seed/klíče peněženky",

View file

@ -929,10 +929,13 @@
"waiting_payment_confirmation": "Warte auf Zahlungsbestätigung",
"wallet": "Geldbörse",
"wallet_group": "Walletgruppe",
"wallet_group_description_existing_seed": "Sie haben für diese Brieftasche einen vorhandenen Samen verwendet. Sie können den Samen erneut überprüfen, wenn Sie ihn bestätigen oder aufschreiben müssen.",
"wallet_group_description_four": "eine Wallet mit einem völlig neuen Seed schaffen.",
"wallet_group_description_one": "In CakeWallet können Sie eine erstellen",
"wallet_group_description_open_wallet": "Andernfalls können Sie die Brieftasche weiter öffnen",
"wallet_group_description_three": "Sehen Sie den Bildschirm zur verfügbaren Wallet und/oder Walletgruppen. Oder wählen",
"wallet_group_description_two": "Durch die Auswahl einer vorhandenen Wallet, mit der ein Seed geteilt werden kann. Jede Walletgruppe kann eine einzelne Wallet jedes Währungstyps enthalten. \n\n Sie können auswählen",
"wallet_group_description_view_seed": "Sie können diesen Samen immer wieder untersuchen",
"wallet_group_empty_state_text_one": "Sieht so aus, als hätten Sie keine kompatiblen Walletgruppen !\n\n TAP",
"wallet_group_empty_state_text_two": "unten, um einen neuen zu machen.",
"wallet_keys": "Wallet-Seed/-Schlüssel",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "Kindly wait for a few seconds for transaction to reflect in transactions history",
"wallet": "Wallet",
"wallet_group": "Wallet Group",
"wallet_group_description_existing_seed": "Youve chosen to use an existing seed for this wallet. You may verify the seed again if you need to confirm or write it down.",
"wallet_group_description_four": "to create a wallet with an entirely new seed.",
"wallet_group_description_one": "In Cake Wallet, you can create a",
"wallet_group_description_open_wallet": "Otherwise, you can continue to open the wallet",
"wallet_group_description_three": "to see the available wallets and/or wallet groups screen. Or choose",
"wallet_group_description_two": "by selecting an existing wallet to share a seed with. Each wallet group can contain a single wallet of each currency type.\n\nYou can select",
"wallet_group_description_view_seed": "You can always view this seed again under",
"wallet_group_empty_state_text_one": "Looks like you don't have any compatible wallet groups!\n\nTap",
"wallet_group_empty_state_text_two": "below to make a new one.",
"wallet_keys": "Wallet seed/keys",

View file

@ -927,10 +927,13 @@
"waitFewSecondForTxUpdate": "Espera unos segundos para que la transacción se refleje en el historial de transacciones.",
"wallet": "Billetera",
"wallet_group": "Grupo de billetera",
"wallet_group_description_existing_seed": "Ha elegido usar una semilla existente para esta billetera. Puede verificar la semilla nuevamente si necesita confirmarla o escribirla.",
"wallet_group_description_four": "Para crear una billetera con una semilla completamente nueva.",
"wallet_group_description_one": "En la billetera de pastel, puedes crear un",
"wallet_group_description_open_wallet": "De lo contrario, puede continuar abriendo la billetera",
"wallet_group_description_three": "Para ver las billeteras disponibles y/o la pantalla de grupos de billeteras. O elegir",
"wallet_group_description_two": "Seleccionando una billetera existente para compartir una semilla con. Cada grupo de billetera puede contener una sola billetera de cada tipo de moneda. \n\n puedes seleccionar",
"wallet_group_description_view_seed": "Siempre puedes ver esta semilla nuevamente debajo",
"wallet_group_empty_state_text_one": "Parece que no tienes ningún grupo de billetera compatible !\n\n toque",
"wallet_group_empty_state_text_two": "a continuación para hacer uno nuevo.",
"wallet_keys": "Billetera semilla/claves",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "Veuillez attendre quelques secondes pour que la transaction soit reflétée dans l'historique des transactions.",
"wallet": "Portefeuille",
"wallet_group": "Groupe de portefeuille",
"wallet_group_description_existing_seed": "Vous avez choisi d'utiliser une graine existante pour ce portefeuille. Vous pouvez vérifier à nouveau la graine si vous avez besoin de le confirmer ou de le noter.",
"wallet_group_description_four": "Pour créer un portefeuille avec une graine entièrement nouvelle.",
"wallet_group_description_one": "Dans Cake Wallet, vous pouvez créer un",
"wallet_group_description_open_wallet": "Sinon, vous pouvez continuer à ouvrir le portefeuille",
"wallet_group_description_three": "Pour voir les portefeuilles et / ou les groupes de portefeuilles disponibles. Ou choisir",
"wallet_group_description_two": "En sélectionnant un portefeuille existant pour partager une graine avec. Chaque groupe de portefeuille peut contenir un seul portefeuille de chaque type de devise. \n\n Vous pouvez sélectionner",
"wallet_group_description_view_seed": "Vous pouvez toujours revoir cette graine sous",
"wallet_group_empty_state_text_one": "On dirait que vous n'avez pas de groupes de portefeuilles compatibles !\n\n Tap",
"wallet_group_empty_state_text_two": "Ci-dessous pour en faire un nouveau.",
"wallet_keys": "Phrase secrète (seed)/Clefs du portefeuille (wallet)",

View file

@ -928,10 +928,13 @@
"waitFewSecondForTxUpdate": "Da fatan za a jira ƴan daƙiƙa don ciniki don yin tunani a tarihin ma'amala",
"wallet": "Zabira",
"wallet_group": "Wallet kungiyar",
"wallet_group_description_existing_seed": "Kun zaɓi yin amfani da iri ɗaya na data kasance don wannan Wallet.you na iya tabbatar da zuriyar kuma idan kuna buƙatar tabbatarwa ko rubuta shi.",
"wallet_group_description_four": "Don ƙirƙirar walat tare da sabon iri.",
"wallet_group_description_one": "A cikin walat walat, zaka iya ƙirƙirar",
"wallet_group_description_open_wallet": "In ba haka ba, zaku iya ci gaba da buɗe walat ɗin",
"wallet_group_description_three": "Don ganin wallets da / ko allon walat din. Ko zabi",
"wallet_group_description_two": "ta hanyar zabar walat mai gudana don raba iri tare da. Kowane rukunin walat na iya ƙunsar watsarin kowane nau'in kuɗi. \n\n Zaka iya zaɓar",
"wallet_group_description_view_seed": "Koyaushe zaka iya duba wannan zuriya",
"wallet_group_empty_state_text_one": "Kamar dai ba ku da wata ƙungiya matattara !\n\n Taɓa",
"wallet_group_empty_state_text_two": "da ke ƙasa don yin sabo.",
"wallet_keys": "Iri/maɓalli na walat",

View file

@ -928,10 +928,13 @@
"waitFewSecondForTxUpdate": "लेन-देन इतिहास में लेन-देन प्रतिबिंबित होने के लिए कृपया कुछ सेकंड प्रतीक्षा करें",
"wallet": "बटुआ",
"wallet_group": "बटुए समूह",
"wallet_group_description_existing_seed": "आपने इस वॉलेट के लिए एक मौजूदा बीज का उपयोग करने के लिए चुना है। यदि आपको इसकी पुष्टि करने या लिखने की आवश्यकता है, तो आप फिर से बीज को सत्यापित कर सकते हैं।",
"wallet_group_description_four": "एक पूरी तरह से नए बीज के साथ एक बटुआ बनाने के लिए।",
"wallet_group_description_one": "केक बटुए में, आप एक बना सकते हैं",
"wallet_group_description_open_wallet": "अन्यथा, आप बटुए खोलना जारी रख सकते हैं",
"wallet_group_description_three": "उपलब्ध वॉलेट और/या वॉलेट समूह स्क्रीन देखने के लिए। या चुनें",
"wallet_group_description_two": "एक बीज साझा करने के लिए एक मौजूदा बटुए का चयन करके। प्रत्येक वॉलेट समूह में प्रत्येक मुद्रा प्रकार का एक एकल वॉलेट हो सकता है।\n\nआप चयन कर सकते हैं",
"wallet_group_description_view_seed": "आप हमेशा इस बीज को फिर से देख सकते हैं",
"wallet_group_empty_state_text_one": "लगता है कि आपके पास कोई संगत बटुआ समूह नहीं है!\n\nनल",
"wallet_group_empty_state_text_two": "नीचे एक नया बनाने के लिए।",
"wallet_keys": "बटुआ बीज / चाबियाँ",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "Pričekajte nekoliko sekundi da se transakcija prikaže u povijesti transakcija",
"wallet": "Novčanik",
"wallet_group": "Skupina novčanika",
"wallet_group_description_existing_seed": "Odlučili ste koristiti postojeće sjeme za ovaj novčanik. Možete ponovno provjeriti sjeme ako ga trebate potvrditi ili zapisati.",
"wallet_group_description_four": "Da biste stvorili novčanik s potpuno novim sjemenom.",
"wallet_group_description_one": "U novčaniku kolača možete stvoriti a",
"wallet_group_description_open_wallet": "Inače možete nastaviti otvarati novčanik",
"wallet_group_description_three": "Da biste vidjeli zaslon dostupnih novčanika i/ili grupa novčanika. Ili odaberite",
"wallet_group_description_two": "Odabirom postojećeg novčanika s kojim ćete dijeliti sjeme. Svaka grupa novčanika može sadržavati jedan novčanik svake vrste valute. \n\n",
"wallet_group_description_view_seed": "Uvijek možete ponovo pogledati ovo sjeme ispod",
"wallet_group_empty_state_text_one": "Izgleda da nemate nikakve kompatibilne grupe novčanika !\n\n",
"wallet_group_empty_state_text_two": "Ispod da napravite novi.",
"wallet_keys": "Pristupni izraz/ključ novčanika",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "Խնդրում ենք սպասել մի քանի վայրկյան, որպեսզի գործարքը արտացոլվի գործարքների պատմության մեջ",
"wallet": "Դրամապանակ",
"wallet_group": "Դրամապանակների խումբ",
"wallet_group_description_existing_seed": "Դուք ընտրել եք օգտագործել այս դրամապանակի համար գոյություն ունեցող սերմը: Կարող եք կրկին հաստատել սերմը, եթե անհրաժեշտ է հաստատել կամ գրել այն:",
"wallet_group_description_four": "Ամբողջովին նոր սերմով դրամապանակ ստեղծելու համար:",
"wallet_group_description_one": "Տորթի դրամապանակում կարող եք ստեղծել ա",
"wallet_group_description_open_wallet": "Հակառակ դեպքում, դուք կարող եք շարունակել բացել դրամապանակը",
"wallet_group_description_three": "Տեսնել առկա դրամապանակներն ու (կամ) դրամապանակների խմբերի էկրանը: Կամ ընտրել",
"wallet_group_description_two": "ընտրելով գոյություն ունեցող դրամապանակ `սերմը կիսելու համար: Դրամապանակների յուրաքանչյուր խումբ կարող է պարունակել յուրաքանչյուր արժույթի տեսակի մեկ դրամապանակ:\n\nԿարող եք ընտրել",
"wallet_group_description_view_seed": "Միշտ կարող եք կրկին դիտել այս սերմը ներքեւում",
"wallet_group_empty_state_text_one": "Կարծես թե որեւէ համատեղելի դրամապանակի խմբեր չունեք:\n\nԹակել",
"wallet_group_empty_state_text_two": "ներքեւում `նորը կազմելու համար:",
"wallet_keys": "Դրամապանակի սերմ/բանալիներ",

View file

@ -929,10 +929,13 @@
"waitFewSecondForTxUpdate": "Mohon tunggu beberapa detik hingga transaksi terlihat di riwayat transaksi",
"wallet": "Dompet",
"wallet_group": "Kelompok dompet",
"wallet_group_description_existing_seed": "Anda telah memilih untuk menggunakan benih yang ada untuk dompet ini. Anda dapat memverifikasi benih lagi jika Anda perlu mengonfirmasi atau menuliskannya.",
"wallet_group_description_four": "Untuk membuat dompet dengan benih yang sama sekali baru.",
"wallet_group_description_one": "Di dompet kue, Anda dapat membuat file",
"wallet_group_description_open_wallet": "Jika tidak, Anda dapat terus membuka dompet",
"wallet_group_description_three": "Untuk melihat layar dompet dan/atau grup dompet yang tersedia. Atau pilih",
"wallet_group_description_two": "dengan memilih dompet yang ada untuk berbagi benih dengan. Setiap grup dompet dapat berisi satu dompet dari setiap jenis mata uang. \n\n Anda dapat memilih",
"wallet_group_description_view_seed": "Anda selalu dapat melihat benih ini lagi di bawah",
"wallet_group_empty_state_text_one": "Sepertinya Anda tidak memiliki grup dompet yang kompatibel !\n\n tap",
"wallet_group_empty_state_text_two": "di bawah ini untuk membuat yang baru.",
"wallet_keys": "Seed/kunci dompet",

View file

@ -929,10 +929,13 @@
"waiting_payment_confirmation": "In attesa di conferma del pagamento",
"wallet": "Portafoglio",
"wallet_group": "Gruppo di portafoglio",
"wallet_group_description_existing_seed": "Hai scelto di utilizzare un seme esistente per questo portafoglio. Puoi verificare di nuovo il seme se devi confermarlo o scriverlo.",
"wallet_group_description_four": "Per creare un portafoglio con un seme completamente nuovo.",
"wallet_group_description_one": "Nel portafoglio di torte, puoi creare un",
"wallet_group_description_open_wallet": "Altrimenti, puoi continuare ad aprire il portafoglio",
"wallet_group_description_three": "Per vedere la schermata di portafogli e/o gruppi di portafogli disponibili. O scegli",
"wallet_group_description_two": "Selezionando un portafoglio esistente con cui condividere un seme. Ogni gruppo di portafoglio può contenere un singolo portafoglio di ciascun tipo di valuta. \n\n È possibile selezionare",
"wallet_group_description_view_seed": "Puoi sempre visualizzare di nuovo questo seme sotto",
"wallet_group_empty_state_text_one": "Sembra che tu non abbia alcun gruppo di portafoglio compatibile !\n\n TAP",
"wallet_group_empty_state_text_two": "Di seguito per crearne uno nuovo.",
"wallet_keys": "Seme Portafoglio /chiavi",

View file

@ -927,10 +927,13 @@
"waitFewSecondForTxUpdate": "取引履歴に取引が反映されるまで数秒お待ちください。",
"wallet": "財布",
"wallet_group": "ウォレットグループ",
"wallet_group_description_existing_seed": "この財布に既存の種子を使用することを選択しました。確認または書き留める必要がある場合は、シードをもう一度確認できます。",
"wallet_group_description_four": "まったく新しい種子の財布を作成します。",
"wallet_group_description_one": "ケーキウォレットでは、aを作成できます",
"wallet_group_description_open_wallet": "それ以外の場合は、ウォレットを開き続けることができます",
"wallet_group_description_three": "利用可能なウォレットおよび/またはウォレットグループの画面を表示します。または選択します",
"wallet_group_description_two": "既存のウォレットを選択して種子を共有します。各ウォレットグループには、各通貨タイプの単一のウォレットを含めることができます。\n\n選択できます",
"wallet_group_description_view_seed": "いつでもこの種を再び見ることができます",
"wallet_group_empty_state_text_one": "互換性のあるウォレットグループがないようです!\n\nタップ",
"wallet_group_empty_state_text_two": "以下に新しいものを作るために。",
"wallet_keys": "ウォレットシード/キー",

View file

@ -512,7 +512,6 @@
"please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.",
"please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.",
"please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"please_select": "선택 해주세요:",
"please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.",
"please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오",
@ -927,10 +926,13 @@
"waitFewSecondForTxUpdate": "거래 내역에 거래가 반영될 때까지 몇 초 정도 기다려 주세요.",
"wallet": "지갑",
"wallet_group": "지갑 그룹",
"wallet_group_description_existing_seed": "이 지갑에 기존 씨앗을 사용하기로 선택했습니다. 확인하거나 작성 해야하는 경우 씨앗을 다시 확인할 수 있습니다.",
"wallet_group_description_four": "완전히 새로운 씨앗으로 지갑을 만듭니다.",
"wallet_group_description_one": "케이크 지갑에서는 a를 만들 수 있습니다",
"wallet_group_description_open_wallet": "그렇지 않으면 지갑을 계속 열 수 있습니다",
"wallet_group_description_three": "사용 가능한 지갑 및/또는 지갑 그룹 스크린을 볼 수 있습니다. 또는 선택하십시오",
"wallet_group_description_two": "씨앗을 공유 할 기존 지갑을 선택함으로써. 각 지갑 그룹은 각 통화 유형의 단일 지갑을 포함 할 수 있습니다. \n\n",
"wallet_group_description_view_seed": "이 씨앗을 언제든지 다시 볼 수 있습니다",
"wallet_group_empty_state_text_one": "호환 지갑 그룹이없는 것 같습니다 !\n\n TAP",
"wallet_group_empty_state_text_two": "아래에서 새로운 것을 만들기 위해.",
"wallet_keys": "지갑 시드 / 키",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "ငွေပေးငွေယူ မှတ်တမ်းတွင် ရောင်ပြန်ဟပ်ရန် စက္ကန့်အနည်းငယ်စောင့်ပါ။",
"wallet": "ပိုက်ဆံအိတ်",
"wallet_group": "ပိုက်ဆံအိတ်အုပ်စု",
"wallet_group_description_existing_seed": "ဒီပိုက်ဆံအိတ်အတွက်ရှိပြီးသားမျိုးစေ့ကိုသုံးဖို့သင်ရွေးချယ်ခဲ့တယ်။ သင်ကအတည်ပြုရန်သို့မဟုတ်ရေးရန်လိုအပ်လျှင်မျိုးစေ့ကိုထပ်မံအတည်ပြုနိုင်သည်။",
"wallet_group_description_four": "လုံးဝအသစ်သောမျိုးစေ့နှင့်အတူပိုက်ဆံအိတ်ဖန်တီးရန်။",
"wallet_group_description_one": "ကိတ်မုန့်၌, သင်တစ် ဦး ဖန်တီးနိုင်ပါတယ်",
"wallet_group_description_open_wallet": "ဒီလိုမှမဟုတ်ရင်သင်ပိုက်ဆံအိတ်ကိုဆက်ဖွင့်နိုင်တယ်",
"wallet_group_description_three": "ရရှိနိုင်သည့်ပိုက်ဆံအိတ်နှင့် / သို့မဟုတ်ပိုက်ဆံအိတ်အုပ်စုများမြင်ကွင်းကိုကြည့်ရှုရန်။ သို့မဟုတ်ရွေးချယ်ပါ",
"wallet_group_description_two": "နှင့်အတူမျိုးစေ့ဝေမျှဖို့ရှိပြီးသားပိုက်ဆံအိတ်တစ်ခုရွေးချယ်ခြင်းအားဖြင့်။ ပိုက်ဆံအိတ်အုပ်စုတစ်ခုစီတွင်ငွေကြေးအမျိုးအစားတစ်ခုစီ၏တစ်ခုတည်းသောပိုက်ဆံအိတ်တစ်ခုပါ 0 င်နိုင်သည်။ \n\n သင်ရွေးချယ်နိုင်သည်",
"wallet_group_description_view_seed": "သင်သည်ဤမျိုးစေ့ကိုနောက်တဖန်ရှုမြင်နိုင်သည်",
"wallet_group_empty_state_text_one": "သင့်တွင်သဟဇာတဖြစ်သောပိုက်ဆံအိတ်အုပ်စုများမရှိပါ။ !\n\n ကိုအသာပုတ်ပါ",
"wallet_group_empty_state_text_two": "အသစ်တစ်ခုကိုတစ်ခုလုပ်ဖို့အောက်တွင်ဖော်ပြထားသော။",
"wallet_keys": "ပိုက်ဆံအိတ် အစေ့/သော့များ",

View file

@ -927,10 +927,13 @@
"waiting_payment_confirmation": "In afwachting van betalingsbevestiging",
"wallet": "Portemonnee",
"wallet_group": "Portemonnee",
"wallet_group_description_existing_seed": "U hebt ervoor gekozen om een bestaand zaadje voor deze portemonnee te gebruiken. U kunt het zaad opnieuw verifiëren als u het moet bevestigen of opschrijven.",
"wallet_group_description_four": "om een portemonnee te maken met een geheel nieuw zaadje.",
"wallet_group_description_one": "In cakeballet kun je een",
"wallet_group_description_open_wallet": "Anders kunt u de portemonnee blijven openen",
"wallet_group_description_three": "Om de beschikbare portefeuilles en/of portefeuillegroepen te zien. Of kies",
"wallet_group_description_two": "Door een bestaande portemonnee te selecteren om een zaadje mee te delen. Elke portemonnee -groep kan een enkele portemonnee van elk valutietype bevatten. \n\n U kunt selecteren",
"wallet_group_description_view_seed": "Je kunt dit zaad altijd opnieuw bekijken",
"wallet_group_empty_state_text_one": "Het lijkt erop dat je geen compatibele portemonnee -groepen hebt !\n\n TAP",
"wallet_group_empty_state_text_two": "hieronder om een nieuwe te maken.",
"wallet_keys": "Portemonnee zaad/sleutels",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "Poczekaj kilka sekund, aż transakcja zostanie odzwierciedlona w historii transakcji",
"wallet": "Portfel",
"wallet_group": "Grupa portfela",
"wallet_group_description_existing_seed": "Zdecydowałeś się użyć istniejącego ziarna do tego portfela. Możesz ponownie zweryfikować ziarno, jeśli chcesz je potwierdzić lub zapisać.",
"wallet_group_description_four": "Aby stworzyć portfel z zupełnie nowym ziarnem.",
"wallet_group_description_one": "W portfelu ciasta możesz stworzyć",
"wallet_group_description_open_wallet": "W przeciwnym razie możesz nadal otwierać portfel",
"wallet_group_description_three": "Aby zobaczyć dostępny ekran portfeli i/lub grup portfeli. Lub wybierz",
"wallet_group_description_two": "Wybierając istniejący portfel do podzielenia nasion. Każda grupa portfela może zawierać pojedynczy portfel każdego typu waluty. \n\n możesz wybrać",
"wallet_group_description_view_seed": "Zawsze możesz ponownie zobaczyć to ziarno pod",
"wallet_group_empty_state_text_one": "Wygląda na to, że nie masz żadnych kompatybilnych grup portfeli !\n\n Tap",
"wallet_group_empty_state_text_two": "poniżej, aby zrobić nowy.",
"wallet_keys": "Klucze portfela",

View file

@ -929,10 +929,13 @@
"waiting_payment_confirmation": "Aguardando confirmação de pagamento",
"wallet": "Carteira",
"wallet_group": "Grupo de carteira",
"wallet_group_description_existing_seed": "Você optou por usar uma semente existente para esta carteira. Você pode verificar a semente novamente se precisar confirmar ou anotá -la.",
"wallet_group_description_four": "Para criar uma carteira com uma semente totalmente nova.",
"wallet_group_description_one": "Na carteira de bolo, você pode criar um",
"wallet_group_description_open_wallet": "Caso contrário, você pode continuar a abrir a carteira",
"wallet_group_description_three": "Para ver as carteiras disponíveis e/ou os grupos de carteiras. Ou escolha",
"wallet_group_description_two": "Selecionando uma carteira existente para compartilhar uma semente. Cada grupo de carteira pode conter uma única carteira de cada tipo de moeda. \n\n você pode selecionar",
"wallet_group_description_view_seed": "Você sempre pode ver esta semente novamente em",
"wallet_group_empty_state_text_one": "Parece que você não tem nenhum grupo de carteira compatível !\n\n Toque",
"wallet_group_empty_state_text_two": "abaixo para fazer um novo.",
"wallet_keys": "Semente/chaves da carteira",

View file

@ -927,10 +927,13 @@
"waitFewSecondForTxUpdate": "Пожалуйста, подождите несколько секунд, чтобы транзакция отразилась в истории транзакций.",
"wallet": "Кошелек",
"wallet_group": "Группа кошелька",
"wallet_group_description_existing_seed": "Вы решили использовать существующее семя для этого кошелька. Вы можете снова проверить семя, если вам нужно подтвердить или записать его.",
"wallet_group_description_four": "создать кошелек с совершенно новым семенем.",
"wallet_group_description_one": "В кошельке для торта вы можете создать",
"wallet_group_description_open_wallet": "В противном случае вы можете продолжать открывать кошелек",
"wallet_group_description_three": "Чтобы увидеть доступные кошельки и/или экраны групп кошельков. Или выберите",
"wallet_group_description_two": "выбирая существующий кошелек, чтобы поделиться семенами. Каждая группа кошелька может содержать один кошелек каждого типа валюты. \n\n Вы можете выбрать",
"wallet_group_description_view_seed": "Вы всегда можете просматривать это семя снова под",
"wallet_group_empty_state_text_one": "Похоже, у вас нет никаких совместимых групп кошелька !\n\n tap",
"wallet_group_empty_state_text_two": "ниже, чтобы сделать новый.",
"wallet_keys": "Мнемоническая фраза/ключи кошелька",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "กรุณารอสักครู่เพื่อให้ธุรกรรมปรากฏในประวัติการทำธุรกรรม",
"wallet": "กระเป๋าสตางค์",
"wallet_group": "กลุ่มกระเป๋าเงิน",
"wallet_group_description_existing_seed": "คุณเลือกที่จะใช้เมล็ดพันธุ์ที่มีอยู่สำหรับกระเป๋าเงินนี้คุณอาจตรวจสอบเมล็ดได้อีกครั้งหากคุณต้องการยืนยันหรือเขียนลงไป",
"wallet_group_description_four": "เพื่อสร้างกระเป๋าเงินที่มีเมล็ดพันธุ์ใหม่ทั้งหมด",
"wallet_group_description_one": "ในกระเป๋าเงินเค้กคุณสามารถสร้างไฟล์",
"wallet_group_description_open_wallet": "มิฉะนั้นคุณสามารถเปิดกระเป๋าเงินต่อไปได้",
"wallet_group_description_three": "หากต้องการดูกระเป๋าเงินและ/หรือกลุ่มกระเป๋าเงินที่มีอยู่ หรือเลือก",
"wallet_group_description_two": "โดยการเลือกกระเป๋าเงินที่มีอยู่เพื่อแบ่งปันเมล็ดด้วย แต่ละกลุ่มกระเป๋าเงินสามารถมีกระเป๋าเงินเดียวของแต่ละประเภทสกุลเงิน \n\n คุณสามารถเลือกได้",
"wallet_group_description_view_seed": "คุณสามารถดูเมล็ดพันธุ์นี้ได้อีกครั้งภายใต้",
"wallet_group_empty_state_text_one": "ดูเหมือนว่าคุณจะไม่มีกลุ่มกระเป๋าเงินที่เข้ากันได้ !\n\n แตะ",
"wallet_group_empty_state_text_two": "ด้านล่างเพื่อสร้างใหม่",
"wallet_keys": "ซีดของกระเป๋า/คีย์",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "Mangyaring maghintay ng ilang segundo para makita ang transaksyon sa history ng mga transaksyon",
"wallet": "Wallet",
"wallet_group": "Group ng Wallet",
"wallet_group_description_existing_seed": "Pinili mong gumamit ng isang umiiral na binhi para sa pitaka na ito. Maaari mong mapatunayan muli ang binhi kung kailangan mong kumpirmahin o isulat ito.",
"wallet_group_description_four": "Upang lumikha ng isang pitaka na may ganap na bagong binhi.",
"wallet_group_description_one": "Sa cake wallet, maaari kang lumikha ng isang",
"wallet_group_description_open_wallet": "Kung hindi man, maaari mong magpatuloy upang buksan ang pitaka",
"wallet_group_description_three": "Upang makita ang magagamit na mga wallets at/o screen ng mga pangkat ng pitaka. O pumili",
"wallet_group_description_two": "Sa pamamagitan ng pagpili ng isang umiiral na pitaka upang magbahagi ng isang binhi. Ang bawat pangkat ng pitaka ay maaaring maglaman ng isang solong pitaka ng bawat uri ng pera.\n\nMaaari kang pumili",
"wallet_group_description_view_seed": "Maaari mong palaging tingnan ang binhi na ito sa ilalim",
"wallet_group_empty_state_text_one": "Mukhang wala kang anumang mga katugmang pangkat ng pitaka!\n\ntap",
"wallet_group_empty_state_text_two": "sa ibaba upang gumawa ng bago.",
"wallet_keys": "Wallet seed/keys",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "İşlemin işlem geçmişine yansıması için lütfen birkaç saniye bekleyin",
"wallet": "Cüzdan",
"wallet_group": "Cüzdan grubu",
"wallet_group_description_existing_seed": "Bu cüzdan için mevcut bir tohum kullanmayı seçtiniz. Onaylamanız veya yazmanız gerekiyorsa tohumu tekrar doğrulayabilirsiniz.",
"wallet_group_description_four": "Tamamen yeni bir tohumla bir cüzdan oluşturmak için.",
"wallet_group_description_one": "Kek cüzdanında bir",
"wallet_group_description_open_wallet": "Aksi takdirde cüzdanı açmaya devam edebilirsiniz",
"wallet_group_description_three": "Mevcut cüzdan ve/veya cüzdan grupları ekranını görmek için. Veya seç",
"wallet_group_description_two": "Bir tohumu paylaşmak için mevcut bir cüzdan seçerek. Her cüzdan grubu, her para türünün tek bir cüzdanı içerebilir. \n\n Seçebilirsiniz",
"wallet_group_description_view_seed": "Bu tohumu her zaman tekrar görebilirsiniz",
"wallet_group_empty_state_text_one": "Herhangi bir uyumlu cüzdan grubunuz yok gibi görünüyor !\n\n TAP",
"wallet_group_empty_state_text_two": "Yeni bir tane yapmak için aşağıda.",
"wallet_keys": "Cüzdan tohumu/anahtarları",

View file

@ -927,10 +927,13 @@
"waitFewSecondForTxUpdate": "Будь ласка, зачекайте кілька секунд, поки транзакція відобразиться в історії транзакцій",
"wallet": "Гаманець",
"wallet_group": "Група гаманців",
"wallet_group_description_existing_seed": "Ви вирішили використовувати існуюче насіння для цього гаманця. Ви можете ще раз перевірити насіння, якщо вам потрібно підтвердити або записати його.",
"wallet_group_description_four": "створити гаманець з абсолютно новим насінням.",
"wallet_group_description_one": "У гаманці тортів ви можете створити a",
"wallet_group_description_open_wallet": "В іншому випадку ви можете продовжувати відкривати гаманець",
"wallet_group_description_three": "Щоб побачити наявні гаманці та/або екран групи гаманців. Або вибрати",
"wallet_group_description_two": "Вибираючи існуючий гаманець, щоб поділитися насінням. Кожна група гаманця може містити один гаманець кожного типу валюти. \n\n Ви можете вибрати",
"wallet_group_description_view_seed": "Ви завжди можете переглянути це насіння ще раз під",
"wallet_group_empty_state_text_one": "Схоже, у вас немає сумісних груп гаманця !\n\n Торкніться",
"wallet_group_empty_state_text_two": "нижче, щоб зробити новий.",
"wallet_keys": "Мнемонічна фраза/ключі гаманця",

View file

@ -928,10 +928,13 @@
"waitFewSecondForTxUpdate": "۔ﮟﯾﺮﮐ ﺭﺎﻈﺘﻧﺍ ﺎﮐ ﮉﻨﮑﯿﺳ ﺪﻨﭼ ﻡﺮﮐ ﮦﺍﺮﺑ ﮯﯿﻟ ﮯﮐ ﮯﻧﺮﮐ ﯽﺳﺎﮑﻋ ﯽﮐ ﻦﯾﺩ ﻦﯿﻟ ﮟﯿﻣ ﺦﯾﺭﺎﺗ ﯽﮐ ﻦ",
"wallet": "پرس",
"wallet_group": "پرس گروپ",
"wallet_group_description_existing_seed": "آپ نے اس پرس کے لئے موجودہ بیج استعمال کرنے کا انتخاب کیا ہے۔ اگر آپ کو اس کی تصدیق یا لکھنے کی ضرورت ہے تو آپ دوبارہ بیج کی تصدیق کرسکتے ہیں۔",
"wallet_group_description_four": "مکمل طور پر نئے بیج کے ساتھ پرس بنانے کے ل.",
"wallet_group_description_one": "کیک پرس میں ، آپ بنا سکتے ہیں",
"wallet_group_description_open_wallet": "بصورت دیگر ، آپ بٹوے کو کھول سکتے ہیں",
"wallet_group_description_three": "دستیاب بٹوے اور/یا پرس گروپوں کی اسکرین کو دیکھنے کے لئے۔ یا منتخب کریں",
"wallet_group_description_two": "بیج کے ساتھ بانٹنے کے لئے موجودہ پرس کا انتخاب کرکے۔ ہر بٹوے گروپ میں ہر کرنسی کی قسم کا ایک بٹوے شامل ہوسکتا ہے۔ \n\n آپ منتخب کرسکتے ہیں",
"wallet_group_description_view_seed": "آپ ہمیشہ اس بیج کو دوبارہ دیکھ سکتے ہیں",
"wallet_group_empty_state_text_one": "ایسا لگتا ہے کہ آپ کے پاس کوئی مطابقت پذیر والیٹ گروپس نہیں ہیں !\n\n نل",
"wallet_group_empty_state_text_two": "ایک نیا بنانے کے لئے ذیل میں.",
"wallet_keys": "بٹوے کے بیج / چابیاں",

View file

@ -925,10 +925,13 @@
"waitFewSecondForTxUpdate": "Vui lòng đợi vài giây để giao dịch được phản ánh trong lịch sử giao dịch",
"wallet": "Cái ví",
"wallet_group": "Nhóm ví",
"wallet_group_description_existing_seed": "Bạn đã chọn sử dụng một hạt giống hiện có cho ví này. Bạn có thể xác minh lại hạt giống nếu bạn cần xác nhận hoặc viết nó ra.",
"wallet_group_description_four": "Để tạo ra một ví với một hạt giống hoàn toàn mới.",
"wallet_group_description_one": "Trong ví bánh, bạn có thể tạo",
"wallet_group_description_open_wallet": "Nếu không, bạn có thể tiếp tục mở ví",
"wallet_group_description_three": "Để xem ví trên ví và/hoặc màn hình nhóm ví. Hoặc chọn",
"wallet_group_description_two": "Bằng cách chọn một ví hiện có để chia sẻ một hạt giống với. Mỗi nhóm ví có thể chứa một ví của mỗi loại tiền tệ. \n\n Bạn có thể chọn",
"wallet_group_description_view_seed": "Bạn luôn có thể xem lại hạt giống này dưới",
"wallet_group_empty_state_text_one": "Có vẻ như bạn không có bất kỳ nhóm ví tương thích nào !\n\n Tap",
"wallet_group_empty_state_text_two": "Dưới đây để làm một cái mới.",
"wallet_keys": "Hạt giống/khóa ví",

View file

@ -927,10 +927,13 @@
"waitFewSecondForTxUpdate": "Fi inurere duro fun awọn iṣeju diẹ fun idunadura lati ṣe afihan ninu itan-akọọlẹ iṣowo",
"wallet": "Ohun apamọwọwọ",
"wallet_group": "Ẹgbẹ apamọwọ",
"wallet_group_description_existing_seed": "O ti yan lati lo irugbin ti o wa tẹlẹ fun ogiriina yii.O le rii daju iru naa lẹẹkansii ti o ba nilo lati jẹrisi tabi kọ silẹ.",
"wallet_group_description_four": "Lati ṣẹda apamọwọ kan pẹlu irugbin tuntun tuntun.",
"wallet_group_description_one": "Ni apamọwọ akara oyinbo, o le ṣẹda a",
"wallet_group_description_open_wallet": "Bibẹẹkọ, o le tẹsiwaju lati ṣii apamọwọ",
"wallet_group_description_three": "Lati wo awọn Woleti ti o wa ati / tabi Iboju Wallt. Tabi yan",
"wallet_group_description_two": "nipa yiyan apamọwọ ti o wa tẹlẹ lati pin irugbin kan pẹlu. Ẹgbẹ apamọwọ kọọkan le ni apamọwọ kan ti iru owo kọọkan. \n\n O le yan",
"wallet_group_description_view_seed": "O le nigbagbogbo wo irugbin yii lẹẹkansi labẹ",
"wallet_group_empty_state_text_one": "O dabi pe o ko ni eyikeyi awọn ẹgbẹ ti o ni ibamu!\n\ntẹ ni kia kia",
"wallet_group_empty_state_text_two": "ni isalẹ lati ṣe ọkan titun.",
"wallet_keys": "Hóró/kọ́kọ́rọ́ àpamọ́wọ́",

View file

@ -926,10 +926,13 @@
"waitFewSecondForTxUpdate": "请等待几秒钟,交易才会反映在交易历史记录中",
"wallet": "钱包",
"wallet_group": "钱包组",
"wallet_group_description_existing_seed": "您已经选择在此钱包中使用现有种子。如果需要确认或写下来,您可能会再次验证种子。",
"wallet_group_description_four": "创建一个带有全新种子的钱包。",
"wallet_group_description_one": "在蛋糕钱包中,您可以创建一个",
"wallet_group_description_open_wallet": "否则,您可以继续打开钱包",
"wallet_group_description_three": "查看可用的钱包和/或钱包组屏幕。或选择",
"wallet_group_description_two": "通过选择现有的钱包与种子共享。每个钱包组都可以包含每种货币类型的单个钱包。\n\n您可以选择",
"wallet_group_description_view_seed": "您可以随时再次在下面查看此种子",
"wallet_group_empty_state_text_one": "看起来您没有任何兼容的钱包组!\n\n tap",
"wallet_group_empty_state_text_two": "下面是一个新的。",
"wallet_keys": "钱包种子/密钥",

1
scripts/android/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
mwebd

View file

@ -23,7 +23,7 @@ MONERO_COM_SCHEME="monero.com"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.22.1"
CAKEWALLET_BUILD_NUMBER=241
CAKEWALLET_BUILD_NUMBER=242
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
CAKEWALLET_SCHEME="cakewallet"

View file

@ -8,50 +8,20 @@ cd "$(dirname "$0")"
NPROC="-j$(nproc)"
if [[ "x$(uname)" == "xDarwin" ]];
then
USE_DOCKER="ON"
NPROC="-j1"
fi
../prepare_moneroc.sh
if [[ ! "x$RUNNER_OS" == "x" ]];
then
REMOVE_CACHES=ON
fi
# NOTE: -j1 is intentional. Otherwise you will run into weird behaviour on macos
if [[ ! "x$USE_DOCKER" == "x" ]];
then
for COIN in monero wownero;
do
pushd ../monero_c
docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} x86_64-linux-android $NPROC"
# docker run --platform linux/amd64 -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} i686-linux-android $NPROC"
docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} armv7a-linux-androideabi $NPROC"
docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} aarch64-linux-android $NPROC"
popd
done
else
for COIN in monero wownero;
do
pushd ../monero_c
env -i ./build_single.sh ${COIN} x86_64-linux-android $NPROC
[[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/x86_64-linux-android
# ./build_single.sh ${COIN} i686-linux-android $NPROC
# [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/i686-linux-android
env -i ./build_single.sh ${COIN} armv7a-linux-androideabi $NPROC
[[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/armv7a-linux-androideabi
env -i ./build_single.sh ${COIN} aarch64-linux-android $NPROC
[[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/aarch64-linux-android
popd
unxz -f ../monero_c/release/${COIN}/x86_64-linux-android_libwallet2_api_c.so.xz
unxz -f ../monero_c/release/${COIN}/armv7a-linux-androideabi_libwallet2_api_c.so.xz
unxz -f ../monero_c/release/${COIN}/aarch64-linux-android_libwallet2_api_c.so.xz
[[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/{built,sources}
done
fi
for COIN in monero wownero;
do
pushd ../monero_c
for target in {x86_64,aarch64}-linux-android armv7a-linux-androideabi
do
if [[ -f "release/${COIN}/${target}_libwallet2_api_c.so" ]];
then
echo "file exist, not building monero_c for ${COIN}/$target.";
else
env -i ./build_single.sh ${COIN} $target $NPROC
unxz -f ../monero_c/release/${COIN}/${target}_libwallet2_api_c.so.xz
fi
done
popd
done

View file

@ -16,7 +16,4 @@ cd mwebd
git reset --hard 555349415f76a42ec5c76152b64c4ab9aabc448f
gomobile bind -target=android -androidapi 21 .
mkdir -p ../../../cw_mweb/android/libs/
mv ./mwebd.aar $_
# cleanup:
cd ..
rm -rf mwebd
cp ./mwebd.aar $_

View file

@ -19,7 +19,7 @@ MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.22.1"
CAKEWALLET_BUILD_NUMBER=288
CAKEWALLET_BUILD_NUMBER=289
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
HAVEN_NAME="Haven"

View file

@ -0,0 +1,148 @@
# Usage:
# docker build . -f Dockerfile.linux -t ghcr.io/cake-tech/cake_wallet:main-linux
# docker push ghcr.io/cake-tech/cake_wallet:main-linux
FROM --platform=linux/amd64 docker.io/debian:12
LABEL org.opencontainers.image.source=https://github.com/cake-tech/cake_wallet
ENV GOLANG_VERSION=1.23.4
# comes from https://developer.android.com/studio/#command-tools
ENV ANDROID_SDK_TOOLS_VERSION=11076708
# https://developer.android.com/studio/releases/build-tools
ENV ANDROID_PLATFORM_VERSION=34
ENV ANDROID_BUILD_TOOLS_VERSION=34.0.0
ENV FLUTTER_VERSION=3.24.0
# If we ever need to migrate the home directory...
RUN sed -i 's|^root:[^:]*:[^:]*:[^:]*:[^:]*:/root:|root:x:0:0:root:/root:|' /etc/passwd
# mkdir -p /root && rm -rf /root && cp -a /root /root
ENV HOME=/root
# Heavily inspired by cirrusci images
# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/tools/Dockerfile
# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/34/Dockerfile
# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/34-ndk/Dockerfile
# https://github.com/cirruslabs/docker-images-flutter/blob/master/sdk/Dockerfile
ENV ANDROID_HOME=/opt/android-sdk-linux \
LANG=en_US.UTF-8 \
LC_ALL=en_US.UTF-8 \
LANGUAGE=en_US:en
ENV ANDROID_SDK_ROOT=$ANDROID_HOME \
PATH=${PATH}:${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/emulator
RUN set -o xtrace \
&& cd /opt \
&& apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y jq \
&& apt-get install -y default-jdk \
&& apt-get install -y sudo wget zip unzip git openssh-client curl bc software-properties-common build-essential ruby-full ruby-bundler libstdc++6 libpulse0 libglu1-mesa locales lcov libsqlite3-dev --no-install-recommends \
# for x86 emulators
&& apt-get install -y libxtst6 libnss3-dev libnspr4 libxss1 libatk-bridge2.0-0 libgtk-3-0 libgdk-pixbuf2.0-0 \
&& apt-get install -y -qq xxd \
&& apt-get install -y lftp \
&& apt-get install -qq -y sqlite3 libsqlite3-dev \
# linux desktop dependencies
&& apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev \
# monero_c dependencies
&& apt-get install -y ccache build-essential autoconf libtool gperf llvm \
# extra stuff for KVM
&& apt-get install -y udev qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils \
# for linux tests
&& apt-get install -y xvfb network-manager ffmpeg x11-utils \
&& rm -rf /var/lib/apt/lists/* \
&& sh -c 'echo "en_US.UTF-8 UTF-8" > /etc/locale.gen' \
&& locale-gen \
&& update-locale LANG=en_US.UTF-8
# install nodejs for actions
RUN apt-get update && \
apt-get install -y curl && \
curl -fsSL https://deb.nodesource.com/setup_23.x | bash - && \
apt-get install -y nodejs && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN wget https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz &&\
rm -rf /usr/local/go &&\
tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-amd64.tar.gz
ENV PATH=${PATH}:/usr/local/go/bin:${HOME}/go/bin
ENV GOROOT=/usr/local/go
ENV GOPATH=${HOME}/go
RUN go install golang.org/x/mobile/cmd/gomobile@latest
RUN gomobile init
RUN wget -q https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_TOOLS_VERSION}_latest.zip -O android-sdk-tools.zip \
&& mkdir -p ${ANDROID_HOME}/cmdline-tools/ \
&& unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}/cmdline-tools/ \
&& mv ${ANDROID_HOME}/cmdline-tools/cmdline-tools ${ANDROID_HOME}/cmdline-tools/latest \
&& chown -R root:root $ANDROID_HOME \
&& rm android-sdk-tools.zip \
&& echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \
&& yes | sdkmanager --licenses \
&& wget -O /usr/bin/android-wait-for-emulator https://raw.githubusercontent.com/travis-ci/travis-cookbooks/master/community-cookbooks/android-sdk/files/default/android-wait-for-emulator \
&& chmod +x /usr/bin/android-wait-for-emulator \
&& sdkmanager platform-tools \
&& mkdir -p ${HOME}/.android \
&& touch ${HOME}/.android/repositories.cfg \
&& git config --global user.email "czarek@cakewallet.com" \
&& git config --global user.name "CakeWallet CI"
# emulator is not available on linux/arm64 (https://issuetracker.google.com/issues/227219818)
RUN if [ $(uname -m) == "x86_64" ]; then sdkmanager emulator ; fi
# Extra dependencies to not download them for cake wallet build
RUN yes | sdkmanager \
"platforms;android-$ANDROID_PLATFORM_VERSION" \
"build-tools;$ANDROID_BUILD_TOOLS_VERSION" \
"platforms;android-33" \
"build-tools;33.0.2" \
"build-tools;33.0.1" \
"build-tools;33.0.0" \
"build-tools;35.0.0"
ENV ANDROID_NDK_VERSION=27.2.12479018
# Extra ndk dependency for sp_scanner
RUN yes | sdkmanager "ndk;$ANDROID_NDK_VERSION" \
"ndk;27.0.12077973"
# https://github.com/ReactiveCircus/android-emulator-runner dependencies for tests
RUN yes | sdkmanager "system-images;android-29;default;x86" \
"system-images;android-29;default;x86_64" \
"system-images;android-31;default;x86_64" \
"platforms;android-29"
# fake the KVM status so android emulator doesn't complain (that much)
RUN (addgroup kvm || true) && \
adduser root kvm && \
mkdir -p /etc/udev/rules.d/ && \
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | tee /etc/udev/rules.d/99-kvm4all.rules
ENV PATH=${HOME}/.cargo/bin:${PATH}
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \
cargo install cargo-ndk && \
for target in aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android x86_64-unknown-linux-gnu; \
do \
rustup target add --toolchain stable $target; \
done
ENV HOME=${HOME}
ENV FLUTTER_HOME=${HOME}/sdks/flutter/${FLUTTER_VERSION}
ENV FLUTTER_ROOT=$FLUTTER_HOME
ENV PATH=${PATH}:${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin
RUN git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME}
RUN yes | flutter doctor --android-licenses \
&& flutter doctor \
&& chown -R root:root ${FLUTTER_HOME}
RUN flutter precache

View file

@ -15,7 +15,7 @@ fi
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="1.12.1"
CAKEWALLET_BUILD_NUMBER=42
CAKEWALLET_BUILD_NUMBER=43
if ! [[ " ${TYPES[*]} " =~ " ${APP_LINUX_TYPE} " ]]; then
echo "Wrong app type."

View file

@ -1,9 +1,5 @@
#!/bin/bash
. ./config.sh
set -x -e
cd "$(dirname "$0")"
@ -15,7 +11,15 @@ NPROC="-j$(nproc)"
for COIN in monero wownero;
do
pushd ../monero_c
./build_single.sh ${COIN} $(gcc -dumpmachine) $NPROC
for target in x86_64-linux-gnu
do
if [[ -f "release/${COIN}/${target}_libwallet2_api_c.so" ]];
then
echo "file exist, not building monero_c for ${COIN}/$target.";
else
./build_single.sh ${COIN} $target $NPROC
unxz -f ../monero_c/release/${COIN}/${target}_libwallet2_api_c.so.xz
fi
done
popd
unxz -f ../monero_c/release/${COIN}/$(gcc -dumpmachine)_libwallet2_api_c.so.xz
done
done

View file

@ -22,7 +22,7 @@ MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="1.15.1"
CAKEWALLET_BUILD_NUMBER=100
CAKEWALLET_BUILD_NUMBER=101
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then

View file

@ -4,9 +4,9 @@ set -x -e
cd "$(dirname "$0")"
if [[ ! -d "monero_c" ]];
if [[ ! -d "monero_c/.git" ]];
then
git clone https://github.com/mrcyjanek/monero_c --branch master
git clone https://github.com/mrcyjanek/monero_c --branch master monero_c
cd monero_c
git checkout af5277f96073917185864d3596e82b67bee54e78
git reset --hard

1
scripts/windows/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
actions-runner

View file

@ -0,0 +1,68 @@
# Usage:
# docker build . -f Dockerfile.windows -t ghcr.io/cake-tech/cake_wallet:main-windows
# docker push ghcr.io/cake-tech/cake_wallet:main-windows
FROM mcr.microsoft.com/windows/servercore:ltsc2022
ENV FLUTTER_VERSION=3.24.0
ENV GIT_VERSION=2.47.1
ENV VS_INSTALLED_DIR="C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools"
ENV PATH="C:\Users\ContainerAdministrator\.cargo\bin;C:\ProgramData\chocolatey\bin;C:\flutter\flutter\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\ContainerAdministrator\AppData\Local\Microsoft\WindowsApps"
ENV RUNNER_VERSION=2.321.0
ENV RUNNER_URL=https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-win-x64-${RUNNER_VERSION}.zip
ENV RUNNER_WORKDIR=_work
RUN powershell -Command \
curl.exe -L https://aka.ms/vs/17/release/vc_redist.x64.exe -o vc_redist.x64.exe ; \
Start-Process -Wait -FilePath .\vc_redist.x64.exe -ArgumentList '/quiet', '/install' ; \
Remove-Item -Force vc_redist.x64.exe
RUN powershell -Command \
$GIT_VERSION = [Environment]::GetEnvironmentVariable('GIT_VERSION'); \
curl.exe -L https://github.com/git-for-windows/git/releases/download/v$($GIT_VERSION).windows.1/Git-$($GIT_VERSION)-64-bit.exe -o git_installer.exe ; \
Start-Process -Wait -FilePath .\git_installer.exe -ArgumentList '/SILENT', '/NOICONS' ; \
Remove-Item -Force git_installer.exe
RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \
Set-ExecutionPolicy RemoteSigned -Scope Process; \
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; \
Invoke-WebRequest https://chocolatey.org/install.ps1 -UseBasicP -OutFile install.ps1; \
powershell -NoProfile -ExecutionPolicy Bypass -File install.ps1; \
Remove-Item -Force install.ps1
RUN choco install -y visualstudio2022community
RUN choco install -y visualstudio2022-workload-nativedesktop
RUN choco install -y nodejs
RUN choco install -y go
RUN choco install -y 7zip
RUN powershell -Command \
curl.exe -L https://win.rustup.rs -o rustup-init.exe; \
Start-Process -Wait -FilePath .\rustup-init.exe -ArgumentList "-y"; \
Remove-Item -Force .\rustup-init.exe
RUN powershell -Command \
curl.exe -L https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -o C:\Windows\System32\nuget.exe
RUN powershell -Command \
$FLUTTER_VERSION = [Environment]::GetEnvironmentVariable('FLUTTER_VERSION'); \
curl.exe -L https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_$($FLUTTER_VERSION)-stable.zip -o flutter.zip ; \
7z x flutter.zip -oC:\flutter -bsp1 -bse1 ; \
Remove-Item -Force flutter.zip
RUN flutter precache
WORKDIR C:\\actions-runner
RUN powershell -Command \
curl.exe -L $env:RUNNER_URL -o 'actions-runner.zip'; \
7z x actions-runner.zip -oC:\actions-runner -bsp1 -bse1 ; \
Remove-Item -Path 'actions-runner.zip'
COPY actions-runner/.credentials /actions-runner/.credentials
COPY actions-runner/.credentials_rsaparams /actions-runner/.credentials_rsaparams
COPY actions-runner/.runner /actions-runner/.runner
COPY ci_entrypoint.ps1 /actions-runner/ci_entrypoint.ps1
ENTRYPOINT ["powershell", "-File", "ci_entrypoint.ps1"]

View file

@ -0,0 +1,5 @@
$runnerDir = "C:\actions-runner"
$runCmd = "$runnerDir\run.cmd"
Write-Host "Starting the runner..."
& $runCmd

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