diff --git a/.gitignore b/.gitignore
index 13b2127afa..4cc00a7e1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,10 +13,7 @@ local.properties
.gradle
# User-specific configurations
-.idea/caches/
-.idea/libraries/
-.idea/*.xml
-.idea/codeStyles
+.idea
# LayoutInspector files
/captures
diff --git a/app/build.gradle b/app/build.gradle
index d94e9cbb30..0b53a8df5e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,17 +2,17 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
-apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.gms.google-services'
+apply plugin: 'com.google.firebase.crashlytics'
android {
- compileSdkVersion 29
+ compileSdkVersion 30
defaultConfig {
applicationId "com.tangem.wallet"
versionCode project.hasProperty('versionCode') ? project.property('versionCode') as int : 1
versionName project.hasProperty('versionName') ? project.property('versionName') : "1.0.0.-SNAPSHOT"
minSdkVersion 21
- targetSdkVersion 29
+ targetSdkVersion 30
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
androidExtensions {
@@ -37,7 +37,7 @@ android {
buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"dev\"'
}
debug_beta {
- initWith debug
+ initWith release
versionNameSuffix "-beta"
applicationIdSuffix ".debug"
buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"prod\"'
@@ -57,7 +57,6 @@ android {
exclude 'lib/x86_64/freebsd/libscrypt.so'
exclude 'lib/x86_64/linux/libscrypt.so'
}
- buildToolsVersion '29.0.3'
}
repositories {
@@ -67,23 +66,25 @@ repositories {
dependencies {
implementation fileTree(include: ['*.aar'], dir: 'libs')
- implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin"
- implementation 'androidx.core:core-ktx:1.3.2'
- implementation 'androidx.appcompat:appcompat:1.2.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
- implementation 'com.google.android.material:material:1.3.0'
- implementation "androidx.core:core-ktx:1.3.2"
- implementation 'com.google.android.play:core:1.10.0'
+ implementation 'androidx.core:core-ktx:1.6.0'
+ implementation 'androidx.appcompat:appcompat:1.3.1'
+ implementation 'androidx.fragment:fragment-ktx:1.3.6'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation "androidx.core:core-ktx:1.6.0"
+ implementation 'com.google.android.play:core:1.10.2'
implementation 'com.google.android.play:core-ktx:1.8.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
- implementation 'com.tangem:blockchain:develop-26'
- implementation 'com.tangem:core:fixes_for_app_release-44'
- implementation 'com.tangem:sdk:fixes_for_app_release-44'
+ implementation 'com.tangem:blockchain:develop-42'
+ implementation 'com.tangem.tangem-sdk-kotlin:core:develop-91'
+ implementation 'com.tangem.tangem-sdk-kotlin:android:develop-91'
// WebView
implementation "androidx.browser:browser:1.3.0"
+ implementation 'com.jakewharton.timber:timber:4.7.1'
+
//lifecycle
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-common-java8:2.3.1"
@@ -114,13 +115,19 @@ dependencies {
// Network and Json
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'com.squareup.retrofit2:converter-moshi:2.6.0'
- implementation 'com.squareup.moshi:moshi:1.9.2'
- implementation "com.squareup.moshi:moshi-kotlin:1.9.2"
+ implementation 'com.squareup.moshi:moshi:1.12.0'
+ implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
+//
+// //Crypto
+ implementation "com.madgag.spongycastle:core:1.58.0.0"
+
+ // animation
+ implementation "com.airbnb.android:lottie:3.4.0"
testImplementation 'junit:junit:4.13.2'
- testImplementation "com.google.truth:truth:1.0.1"
- androidTestImplementation 'androidx.test.ext:junit:1.1.2'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+ testImplementation "com.google.truth:truth:1.1.3"
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 97e4157f97..c2d4929889 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -81,9 +81,33 @@
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/assets/binance_tokens.json b/app/src/main/assets/binance_tokens.json
new file mode 100644
index 0000000000..08ed8356fa
--- /dev/null
+++ b/app/src/main/assets/binance_tokens.json
@@ -0,0 +1,3086 @@
+[
+ {
+ "symbol" : "BUSD",
+ "decimalCount" : 8,
+ "contractAddress" : "BUSD-BD1",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BUSD-BD1/logo.png",
+ "name" : "Binance USD"
+ },
+ {
+ "symbol" : "BTCB",
+ "decimalCount" : 8,
+ "contractAddress" : "BTCB-1DE",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BTCB-1DE/logo.png",
+ "name" : "Bitcoin BEP2"
+ },
+ {
+ "symbol" : "ADA",
+ "decimalCount" : 8,
+ "contractAddress" : "ADA-9F4",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ADA-9F4/logo.png",
+ "name" : "CardanoBEP2"
+ },
+ {
+ "symbol" : "AERGO",
+ "decimalCount" : 8,
+ "contractAddress" : "AERGO-46B",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/AERGO-46B/logo.png",
+ "name" : "Aergo"
+ },
+ {
+ "symbol" : "ANKR",
+ "decimalCount" : 8,
+ "contractAddress" : "ANKR-E97",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ANKR-E97/logo.png",
+ "name" : "ANKR"
+ },
+ {
+ "symbol" : "ARN",
+ "decimalCount" : 8,
+ "contractAddress" : "ARN-71B",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ARN-71B/logo.png",
+ "name" : "Aeron"
+ },
+ {
+ "symbol" : "ARPA",
+ "decimalCount" : 8,
+ "contractAddress" : "ARPA-575",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ARPA-575/logo.png",
+ "name" : "ARPA"
+ },
+ {
+ "symbol" : "BZNT",
+ "decimalCount" : 8,
+ "contractAddress" : "BZNT-464",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BZNT-464/logo.png",
+ "name" : "Bezant Token"
+ },
+ {
+ "symbol" : "DAI",
+ "decimalCount" : 8,
+ "contractAddress" : "DAI-D75",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DAI-D75/logo.png",
+ "name" : "DaiBEP2"
+ },
+ {
+ "symbol" : "ETH",
+ "decimalCount" : 8,
+ "contractAddress" : "ETH-1C9",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ETH-1C9/logo.png",
+ "name" : "ETH BEP2"
+ },
+ {
+ "symbol" : "ART",
+ "decimalCount" : 8,
+ "contractAddress" : "ART-3C9",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ART-3C9/logo.png",
+ "name" : "Maecenas ART Token"
+ },
+ {
+ "symbol" : "ATOM",
+ "decimalCount" : 8,
+ "contractAddress" : "ATOM-596",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ATOM-596/logo.png",
+ "name" : "ATOMBEP2"
+ },
+ {
+ "symbol" : "ATP",
+ "decimalCount" : 8,
+ "contractAddress" : "ATP-38C",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ATP-38C/logo.png",
+ "name" : "Atlas Protocol"
+ },
+ {
+ "symbol" : "CAKE",
+ "decimalCount" : 8,
+ "contractAddress" : "CAKE-435",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CAKE-435/logo.png",
+ "name" : "PancakeSwap Token"
+ },
+ {
+ "symbol" : "CAN",
+ "decimalCount" : 8,
+ "contractAddress" : "CAN-677",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CAN-677/logo.png",
+ "name" : "CanYaCoin"
+ },
+ {
+ "symbol" : "CAS",
+ "decimalCount" : 8,
+ "contractAddress" : "CAS-167",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CAS-167/logo.png",
+ "name" : "CASHAA"
+ },
+ {
+ "symbol" : "CBIX",
+ "decimalCount" : 8,
+ "contractAddress" : "CBIX-3C9",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CBIX-3C9/logo.png",
+ "name" : "Cubiex"
+ },
+ {
+ "symbol" : "CBM",
+ "decimalCount" : 8,
+ "contractAddress" : "CBM-4B2",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CBM-4B2/logo.png",
+ "name" : "CryptoBonusMiles"
+ },
+ {
+ "symbol" : "CHZ",
+ "decimalCount" : 8,
+ "contractAddress" : "CHZ-ECD",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CHZ-ECD/logo.png",
+ "name" : "Chiliz"
+ },
+ {
+ "symbol" : "CNNS",
+ "decimalCount" : 8,
+ "contractAddress" : "CNNS-E16",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CNNS-E16/logo.png",
+ "name" : "Crypto Neo-value Neural System"
+ },
+ {
+ "symbol" : "COS",
+ "decimalCount" : 8,
+ "contractAddress" : "COS-2E4",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/COS-2E4/logo.png",
+ "name" : "Contentos"
+ },
+ {
+ "symbol" : "COTI",
+ "decimalCount" : 8,
+ "contractAddress" : "COTI-CBB",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/COTI-CBB/logo.png",
+ "name" : "COTI"
+ },
+ {
+ "symbol" : "COVA",
+ "decimalCount" : 8,
+ "contractAddress" : "COVA-218",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/COVA-218/logo.png",
+ "name" : "Covalent Token"
+ },
+ {
+ "symbol" : "CRPT",
+ "decimalCount" : 8,
+ "contractAddress" : "CRPT-8C9",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CRPT-8C9/logo.png",
+ "name" : "Crypterium Token"
+ },
+ {
+ "symbol" : "IDRTB",
+ "decimalCount" : 8,
+ "contractAddress" : "IDRTB-178",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/IDRTB-178/logo.png",
+ "name" : "Rupiah Token"
+ },
+ {
+ "symbol" : "AVA",
+ "decimalCount" : 8,
+ "contractAddress" : "AVA-645",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/AVA-645/logo.png",
+ "name" : "Travala.com Token"
+ },
+ {
+ "symbol" : "AWC",
+ "decimalCount" : 8,
+ "contractAddress" : "AWC-986",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/AWC-986/logo.png",
+ "name" : "Atomic Wallet Token"
+ },
+ {
+ "symbol" : "AXPR",
+ "decimalCount" : 8,
+ "contractAddress" : "AXPR-777",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/AXPR-777/logo.png",
+ "name" : "AXPR.B"
+ },
+ {
+ "symbol" : "BAKE",
+ "decimalCount" : 8,
+ "contractAddress" : "BAKE-5E0",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BAKE-5E0/logo.png",
+ "name" : "BakeryToken"
+ },
+ {
+ "symbol" : "BAND",
+ "decimalCount" : 8,
+ "contractAddress" : "BAND-34B",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BAND-34B/logo.png",
+ "name" : "BandProtocolBEP2"
+ },
+ {
+ "symbol" : "BCH",
+ "decimalCount" : 8,
+ "contractAddress" : "BCH-1FD",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BCH-1FD/logo.png",
+ "name" : "BCH BEP2"
+ },
+ {
+ "symbol" : "BCPT",
+ "decimalCount" : 8,
+ "contractAddress" : "BCPT-95A",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BCPT-95A/logo.png",
+ "name" : "Blockmason Credit Protocol"
+ },
+ {
+ "symbol" : "BEAR",
+ "decimalCount" : 8,
+ "contractAddress" : "BEAR-14C",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BEAR-14C/logo.png",
+ "name" : "3X Short Bitcoin Token"
+ },
+ {
+ "symbol" : "BET",
+ "decimalCount" : 8,
+ "contractAddress" : "BET-844",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BET-844/logo.png",
+ "name" : "EOSBet Token"
+ },
+ {
+ "symbol" : "TAUDB",
+ "decimalCount" : 8,
+ "contractAddress" : "TAUDB-888",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TAUDB-888/logo.png",
+ "name" : "TrueAUD"
+ },
+ {
+ "symbol" : "THKDB",
+ "decimalCount" : 8,
+ "contractAddress" : "THKDB-888",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/THKDB-888/logo.png",
+ "name" : "TrueHKD"
+ },
+ {
+ "symbol" : "TUSDB",
+ "decimalCount" : 8,
+ "contractAddress" : "TUSDB-888",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TUSDB-888/logo.png",
+ "name" : "TrueUSD"
+ },
+ {
+ "symbol" : "USDSB",
+ "decimalCount" : 8,
+ "contractAddress" : "USDSB-1AC",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/USDSB-1AC/logo.png",
+ "name" : "USDS"
+ },
+ {
+ "symbol" : "USDT",
+ "decimalCount" : 8,
+ "contractAddress" : "USDT-6D8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/USDT-6D8/logo.png",
+ "name" : "USDTBEP2"
+ },
+ {
+ "symbol" : "BIFI",
+ "decimalCount" : 8,
+ "contractAddress" : "BIFI-290",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BIFI-290/logo.png",
+ "name" : "beefy.finance"
+ },
+ {
+ "symbol" : "BKBT",
+ "decimalCount" : 8,
+ "contractAddress" : "BKBT-3A6",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BKBT-3A6/logo.png",
+ "name" : "Bitwires Token"
+ },
+ {
+ "symbol" : "BLINK",
+ "decimalCount" : 8,
+ "contractAddress" : "BLINK-9C6",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BLINK-9C6/logo.png",
+ "name" : "Blockmason Link"
+ },
+ {
+ "symbol" : "BOLT",
+ "decimalCount" : 8,
+ "contractAddress" : "BOLT-4C6",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BOLT-4C6/logo.png",
+ "name" : "BOLT Token"
+ },
+ {
+ "symbol" : "BST2",
+ "decimalCount" : 8,
+ "contractAddress" : "BST2-2F2",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BST2-2F2/logo.png",
+ "name" : "BOOSTO"
+ },
+ {
+ "symbol" : "BTTB",
+ "decimalCount" : 8,
+ "contractAddress" : "BTTB-D31",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BTTB-D31/logo.png",
+ "name" : "BTTB"
+ },
+ {
+ "symbol" : "BULL",
+ "decimalCount" : 8,
+ "contractAddress" : "BULL-BE4",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BULL-BE4/logo.png",
+ "name" : "3x Long Bitcoin Token"
+ },
+ {
+ "symbol" : "BURGER",
+ "decimalCount" : 8,
+ "contractAddress" : "BURGER-33A",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/BURGER-33A/logo.png",
+ "name" : "Burger Swap"
+ },
+ {
+ "symbol" : "CSM",
+ "decimalCount" : 8,
+ "contractAddress" : "CSM-734",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/CSM-734/logo.png",
+ "name" : "“Consentium”"
+ },
+ {
+ "symbol" : "DEFI",
+ "decimalCount" : 8,
+ "contractAddress" : "DEFI-FA5",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DEFI-FA5/logo.png",
+ "name" : "DeFi Token"
+ },
+ {
+ "symbol" : "DOGE",
+ "decimalCount" : 8,
+ "contractAddress" : "DOGE-B67",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DOGE-B67/logo.png",
+ "name" : "Dogecoin"
+ },
+ {
+ "symbol" : "DOS",
+ "decimalCount" : 8,
+ "contractAddress" : "DOS-120",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DOS-120/logo.png",
+ "name" : "DOS Network Token"
+ },
+ {
+ "symbol" : "DOT",
+ "decimalCount" : 8,
+ "contractAddress" : "DOT-64C",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DOT-64C/logo.png",
+ "name" : "PolkadotBEP2"
+ },
+ {
+ "symbol" : "DREP",
+ "decimalCount" : 8,
+ "contractAddress" : "DREP-7D2",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DREP-7D2/logo.png",
+ "name" : "DREP"
+ },
+ {
+ "symbol" : "DUSK",
+ "decimalCount" : 8,
+ "contractAddress" : "DUSK-45E",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/DUSK-45E/logo.png",
+ "name" : "Dusk Network"
+ },
+ {
+ "symbol" : "EBST",
+ "decimalCount" : 8,
+ "contractAddress" : "EBST-783",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EBST-783/logo.png",
+ "name" : "eBoost"
+ },
+ {
+ "symbol" : "ENTRP",
+ "decimalCount" : 8,
+ "contractAddress" : "ENTRP-C8D",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ENTRP-C8D/logo.png",
+ "name" : "Hut34 Entropy"
+ },
+ {
+ "symbol" : "EOS",
+ "decimalCount" : 8,
+ "contractAddress" : "EOS-CDD",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EOS-CDD/logo.png",
+ "name" : "EOS BEP2"
+ },
+ {
+ "symbol" : "EOSBEAR",
+ "decimalCount" : 8,
+ "contractAddress" : "EOSBEAR-721",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EOSBEAR-721/logo.png",
+ "name" : "3X Short EOS Token"
+ },
+ {
+ "symbol" : "EOSBULL",
+ "decimalCount" : 8,
+ "contractAddress" : "EOSBULL-F0D",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EOSBULL-F0D/logo.png",
+ "name" : "3X Long EOS Token"
+ },
+ {
+ "symbol" : "EQL",
+ "decimalCount" : 8,
+ "contractAddress" : "EQL-586",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EQL-586/logo.png",
+ "name" : "EQUAL"
+ },
+ {
+ "symbol" : "ERD",
+ "decimalCount" : 8,
+ "contractAddress" : "ERD-D06",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ERD-D06/logo.png",
+ "name" : "Elrond"
+ },
+ {
+ "symbol" : "ETHBEAR",
+ "decimalCount" : 8,
+ "contractAddress" : "ETHBEAR-B2B",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ETHBEAR-B2B/logo.png",
+ "name" : "3X Short Ethereum Token"
+ },
+ {
+ "symbol" : "ETHBULL",
+ "decimalCount" : 8,
+ "contractAddress" : "ETHBULL-D33",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ETHBULL-D33/logo.png",
+ "name" : "3X Long Ethereum Token"
+ },
+ {
+ "symbol" : "EVT",
+ "decimalCount" : 8,
+ "contractAddress" : "EVT-49B",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/EVT-49B/logo.png",
+ "name" : "everiToken"
+ },
+ {
+ "symbol" : "FRM",
+ "decimalCount" : 8,
+ "contractAddress" : "FRM-DE7",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/FRM-DE7/logo.png",
+ "name" : "Ferrum Network Token"
+ },
+ {
+ "symbol" : "FSN",
+ "decimalCount" : 8,
+ "contractAddress" : "FSN-E14",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/FSN-E14/logo.png",
+ "name" : "Fusion"
+ },
+ {
+ "symbol" : "FTM",
+ "decimalCount" : 8,
+ "contractAddress" : "FTM-A64",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/FTM-A64/logo.png",
+ "name" : "Fantom"
+ },
+ {
+ "symbol" : "GIV",
+ "decimalCount" : 8,
+ "contractAddress" : "GIV-94E",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/GIV-94E/logo.png",
+ "name" : "Givly Coin"
+ },
+ {
+ "symbol" : "GMAT",
+ "decimalCount" : 8,
+ "contractAddress" : "GMAT-FC8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/GMAT-FC8/logo.png",
+ "name" : "GoWithMi"
+ },
+ {
+ "symbol" : "GTO",
+ "decimalCount" : 8,
+ "contractAddress" : "GTO-908",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/GTO-908/logo.png",
+ "name" : "Gifto"
+ },
+ {
+ "symbol" : "HNST",
+ "decimalCount" : 8,
+ "contractAddress" : "HNST-3C9",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/HNST-3C9/logo.png",
+ "name" : "Honest"
+ },
+ {
+ "symbol" : "HYN",
+ "decimalCount" : 8,
+ "contractAddress" : "HYN-F21",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/HYN-F21/logo.png",
+ "name" : "Hyperion Token"
+ },
+ {
+ "symbol" : "INJ",
+ "decimalCount" : 8,
+ "contractAddress" : "INJ-FAE",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/INJ-FAE/logo.png",
+ "name" : "Injective Protocol"
+ },
+ {
+ "symbol" : "IRIS",
+ "decimalCount" : 8,
+ "contractAddress" : "IRIS-D88",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/IRIS-D88/logo.png",
+ "name" : "IRIS Network"
+ },
+ {
+ "symbol" : "KAT",
+ "decimalCount" : 8,
+ "contractAddress" : "KAT-7BB",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/KAT-7BB/logo.png",
+ "name" : "Kambria Token"
+ },
+ {
+ "symbol" : "LBA",
+ "decimalCount" : 8,
+ "contractAddress" : "LBA-340",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/LBA-340/logo.png",
+ "name" : "Lend-Borrow-Asset"
+ },
+ {
+ "symbol" : "LIT",
+ "decimalCount" : 8,
+ "contractAddress" : "LIT-099",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/LIT-099/logo.png",
+ "name" : "LITION"
+ },
+ {
+ "symbol" : "LOKI",
+ "decimalCount" : 8,
+ "contractAddress" : "LOKI-6A9",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/LOKI-6A9/logo.png",
+ "name" : "Loki"
+ },
+ {
+ "symbol" : "LTC",
+ "decimalCount" : 8,
+ "contractAddress" : "LTC-F07",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/LTC-F07/logo.png",
+ "name" : "LTC BEP2"
+ },
+ {
+ "symbol" : "LTO",
+ "decimalCount" : 8,
+ "contractAddress" : "LTO-BDF",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/LTO-BDF/logo.png",
+ "name" : "LTO Network"
+ },
+ {
+ "symbol" : "MATIC",
+ "decimalCount" : 8,
+ "contractAddress" : "MATIC-84A",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MATIC-84A/logo.png",
+ "name" : "Matic Token"
+ },
+ {
+ "symbol" : "MDAB",
+ "decimalCount" : 8,
+ "contractAddress" : "MDAB-D42",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MDAB-D42/logo.png",
+ "name" : "MDAB"
+ },
+ {
+ "symbol" : "MEETONE",
+ "decimalCount" : 8,
+ "contractAddress" : "MEETONE-031",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MEETONE-031/logo.png",
+ "name" : "MEET.ONE"
+ },
+ {
+ "symbol" : "MITH",
+ "decimalCount" : 8,
+ "contractAddress" : "MITH-C76",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MITH-C76/logo.png",
+ "name" : "Mithril"
+ },
+ {
+ "symbol" : "MITX",
+ "decimalCount" : 8,
+ "contractAddress" : "MITX-CAA",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MITX-CAA/logo.png",
+ "name" : "Morpheus Infrastructure Token"
+ },
+ {
+ "symbol" : "MTV",
+ "decimalCount" : 8,
+ "contractAddress" : "MTV-4C6",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MTV-4C6/logo.png",
+ "name" : "MultiVAC"
+ },
+ {
+ "symbol" : "MVL",
+ "decimalCount" : 8,
+ "contractAddress" : "MVL-7B0",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MVL-7B0/logo.png",
+ "name" : "Mass Vehicle Ledger"
+ },
+ {
+ "symbol" : "MZK",
+ "decimalCount" : 8,
+ "contractAddress" : "MZK-2C7",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MZK-2C7/logo.png",
+ "name" : "Muzika"
+ },
+ {
+ "symbol" : "NEW",
+ "decimalCount" : 8,
+ "contractAddress" : "NEW-09E",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NEW-09E/logo.png",
+ "name" : "NEWTON"
+ },
+ {
+ "symbol" : "NEXO",
+ "decimalCount" : 8,
+ "contractAddress" : "NEXO-A84",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NEXO-A84/logo.png",
+ "name" : "Nexo"
+ },
+ {
+ "symbol" : "NOIZB",
+ "decimalCount" : 8,
+ "contractAddress" : "NOIZB-878",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NOIZB-878/logo.png",
+ "name" : "NOIZ Token"
+ },
+ {
+ "symbol" : "NOW",
+ "decimalCount" : 8,
+ "contractAddress" : "NOW-E68",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NOW-E68/logo.png",
+ "name" : "NOW Token"
+ },
+ {
+ "symbol" : "NPXB",
+ "decimalCount" : 8,
+ "contractAddress" : "NPXB-1E8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NPXB-1E8/logo.png",
+ "name" : "NPX Binance token"
+ },
+ {
+ "symbol" : "NPXSXEM",
+ "decimalCount" : 8,
+ "contractAddress" : "NPXSXEM-89C",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/NPXSXEM-89C/logo.png",
+ "name" : "Pundi X NEM"
+ },
+ {
+ "symbol" : "ONE",
+ "decimalCount" : 8,
+ "contractAddress" : "ONE-5F9",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ONE-5F9/logo.png",
+ "name" : "Harmony.One"
+ },
+ {
+ "symbol" : "ONT",
+ "decimalCount" : 8,
+ "contractAddress" : "ONT-33D",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ONT-33D/logo.png",
+ "name" : "ONTBEP2"
+ },
+ {
+ "symbol" : "PHB",
+ "decimalCount" : 8,
+ "contractAddress" : "PHB-2DF",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PHB-2DF/logo.png",
+ "name" : "Red Pulse Phoenix Binance"
+ },
+ {
+ "symbol" : "PHV",
+ "decimalCount" : 8,
+ "contractAddress" : "PHV-4A1",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PHV-4A1/logo.png",
+ "name" : "PathHive Network"
+ },
+ {
+ "symbol" : "PIBNB",
+ "decimalCount" : 8,
+ "contractAddress" : "PIBNB-43C",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PIBNB-43C/logo.png",
+ "name" : "PCHAIN Token"
+ },
+ {
+ "symbol" : "PLG",
+ "decimalCount" : 8,
+ "contractAddress" : "PLG-D8D",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PLG-D8D/logo.png",
+ "name" : "Pledge Coin"
+ },
+ {
+ "symbol" : "PVT",
+ "decimalCount" : 8,
+ "contractAddress" : "PVT-554",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PVT-554/logo.png",
+ "name" : "Pivot Token"
+ },
+ {
+ "symbol" : "PYN",
+ "decimalCount" : 8,
+ "contractAddress" : "PYN-C37",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/PYN-C37/logo.png",
+ "name" : "paycentos"
+ },
+ {
+ "symbol" : "QBX",
+ "decimalCount" : 8,
+ "contractAddress" : "QBX-38C",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/QBX-38C/logo.png",
+ "name" : "qiibeeToken"
+ },
+ {
+ "symbol" : "RAVEN",
+ "decimalCount" : 8,
+ "contractAddress" : "RAVEN-F66",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/RAVEN-F66/logo.png",
+ "name" : "Raven Protocol"
+ },
+ {
+ "symbol" : "RUNE",
+ "decimalCount" : 8,
+ "contractAddress" : "RUNE-B1A",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/RUNE-B1A/logo.png",
+ "name" : "Rune"
+ },
+ {
+ "symbol" : "SHR",
+ "decimalCount" : 8,
+ "contractAddress" : "SHR-DB6",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SHR-DB6/logo.png",
+ "name" : "ShareToken"
+ },
+ {
+ "symbol" : "SLV",
+ "decimalCount" : 8,
+ "contractAddress" : "SLV-986",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SLV-986/logo.png",
+ "name" : "Silverway"
+ },
+ {
+ "symbol" : "SPNDB",
+ "decimalCount" : 8,
+ "contractAddress" : "SPNDB-916",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SPNDB-916/logo.png",
+ "name" : "Spendcoin"
+ },
+ {
+ "symbol" : "STYL",
+ "decimalCount" : 8,
+ "contractAddress" : "STYL-65B",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/STYL-65B/logo.png",
+ "name" : "Yin Lang Music IP Token"
+ },
+ {
+ "symbol" : "SWINGBY",
+ "decimalCount" : 8,
+ "contractAddress" : "SWINGBY-888",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SWINGBY-888/logo.png",
+ "name" : "Swingby Token"
+ },
+ {
+ "symbol" : "SWIPE.B",
+ "decimalCount" : 8,
+ "contractAddress" : "SWIPE.B-DC0",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SWIPE.B-DC0/logo.png",
+ "name" : "SWIPE Token"
+ },
+ {
+ "symbol" : "SXP",
+ "decimalCount" : 8,
+ "contractAddress" : "SXP-CCC",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/SXP-CCC/logo.png",
+ "name" : "Swipe"
+ },
+ {
+ "symbol" : "TM2",
+ "decimalCount" : 8,
+ "contractAddress" : "TM2-0C4",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TM2-0C4/logo.png",
+ "name" : "Traxia 2"
+ },
+ {
+ "symbol" : "TOMOB",
+ "decimalCount" : 8,
+ "contractAddress" : "TOMOB-4BC",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TOMOB-4BC/logo.png",
+ "name" : "TomoChain"
+ },
+ {
+ "symbol" : "TOP",
+ "decimalCount" : 8,
+ "contractAddress" : "TOP-491",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TOP-491/logo.png",
+ "name" : "TOP Network"
+ },
+ {
+ "symbol" : "TROY",
+ "decimalCount" : 8,
+ "contractAddress" : "TROY-9B8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TROY-9B8/logo.png",
+ "name" : "TROY"
+ },
+ {
+ "symbol" : "TRUE",
+ "decimalCount" : 8,
+ "contractAddress" : "TRUE-D84",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TRUE-D84/logo.png",
+ "name" : "TrueChain"
+ },
+ {
+ "symbol" : "TRXB",
+ "decimalCount" : 8,
+ "contractAddress" : "TRXB-2E6",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TRXB-2E6/logo.png",
+ "name" : "TRXB"
+ },
+ {
+ "symbol" : "TWT",
+ "decimalCount" : 8,
+ "contractAddress" : "TWT-8C2",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/TWT-8C2/logo.png",
+ "name" : "Trust Wallet"
+ },
+ {
+ "symbol" : "UGAS",
+ "decimalCount" : 8,
+ "contractAddress" : "UGAS-B0C",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/UGAS-B0C/logo.png",
+ "name" : "Ultrain Coin"
+ },
+ {
+ "symbol" : "UND",
+ "decimalCount" : 8,
+ "contractAddress" : "UND-EBC",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/UND-EBC/logo.png",
+ "name" : "United Network Distribution"
+ },
+ {
+ "symbol" : "UPX",
+ "decimalCount" : 8,
+ "contractAddress" : "UPX-F3E",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/UPX-F3E/logo.png",
+ "name" : "UPX"
+ },
+ {
+ "symbol" : "VDX",
+ "decimalCount" : 8,
+ "contractAddress" : "VDX-A17",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/VDX-A17/logo.png",
+ "name" : "Vodi X"
+ },
+ {
+ "symbol" : "VIDT",
+ "decimalCount" : 8,
+ "contractAddress" : "VIDT-F53",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/VIDT-F53/logo.png",
+ "name" : "V-ID Token"
+ },
+ {
+ "symbol" : "VOTE",
+ "decimalCount" : 8,
+ "contractAddress" : "VOTE-FD4",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/VOTE-FD4/logo.png",
+ "name" : "Vote"
+ },
+ {
+ "symbol" : "WICC",
+ "decimalCount" : 8,
+ "contractAddress" : "WICC-01D",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/WICC-01D/logo.png",
+ "name" : "WaykiChain Coin"
+ },
+ {
+ "symbol" : "WINB",
+ "decimalCount" : 8,
+ "contractAddress" : "WINB-41F",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/WINB-41F/logo.png",
+ "name" : "WINB"
+ },
+ {
+ "symbol" : "WISH",
+ "decimalCount" : 8,
+ "contractAddress" : "WISH-2D5",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/WISH-2D5/logo.png",
+ "name" : "MyWish"
+ },
+ {
+ "symbol" : "WRX",
+ "decimalCount" : 8,
+ "contractAddress" : "WRX-ED1",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/WRX-ED1/logo.png",
+ "name" : "WazirX Token"
+ },
+ {
+ "symbol" : "XBASE",
+ "decimalCount" : 8,
+ "contractAddress" : "XBASE-CD2",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XBASE-CD2/logo.png",
+ "name" : "Eterbase Coin"
+ },
+ {
+ "symbol" : "XRP",
+ "decimalCount" : 8,
+ "contractAddress" : "XRP-BF2",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XRP-BF2/logo.png",
+ "name" : "XRP BEP2"
+ },
+ {
+ "symbol" : "XRPBEAR",
+ "decimalCount" : 8,
+ "contractAddress" : "XRPBEAR-00B",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XRPBEAR-00B/logo.png",
+ "name" : "3X Short XRP Token"
+ },
+ {
+ "symbol" : "XRPBULL",
+ "decimalCount" : 8,
+ "contractAddress" : "XRPBULL-E7C",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XRPBULL-E7C/logo.png",
+ "name" : "3X Long XRP Token"
+ },
+ {
+ "symbol" : "XTZ",
+ "decimalCount" : 8,
+ "contractAddress" : "XTZ-F7A",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XTZ-F7A/logo.png",
+ "name" : "XTZ BEP2"
+ },
+ {
+ "symbol" : "XVS",
+ "decimalCount" : 8,
+ "contractAddress" : "XVS-795",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/XVS-795/logo.png",
+ "name" : "Venus"
+ },
+ {
+ "symbol" : "YFII",
+ "decimalCount" : 8,
+ "contractAddress" : "YFII-061",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/YFII-061/logo.png",
+ "name" : "YFIIBEP2"
+ },
+ {
+ "symbol" : "ZEBI",
+ "decimalCount" : 8,
+ "contractAddress" : "ZEBI-84F",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ZEBI-84F/logo.png",
+ "name" : "ZEBI"
+ },
+ {
+ "symbol" : "ZEC",
+ "decimalCount" : 8,
+ "contractAddress" : "ZEC-93E",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/ZEC-93E/logo.png",
+ "name" : "ZECBEP2"
+ },
+ {
+ "contractAddress": "NMX-7AFM",
+ "symbol": "NMX",
+ "name": "NOMINEX",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "GTMRS-D96M",
+ "symbol": "GTMRS",
+ "name": "Getmoders",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "DRC-FF4M",
+ "symbol": "DRC",
+ "name": "DARIK COIN",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "IMC-D5AM",
+ "symbol": "IMC",
+ "name": "IMC",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "JPIN-89AM",
+ "symbol": "JPIN",
+ "name": "JADEPIN",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "TBCC",
+ "symbol": "TBCC",
+ "name": "TBC",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "SZAR-952M",
+ "symbol": "SZAR",
+ "name": "SMART ZAR",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "SOUL-D11M",
+ "symbol": "SOUL",
+ "name": "CryptoSoul",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "VTBC-C26M",
+ "symbol": "VTBC",
+ "name": "TBCCVPN",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "LGVM-0F6M",
+ "symbol": "LGVM",
+ "name": "Longevium",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "JUV-FC7M",
+ "symbol": "JUV",
+ "name": "Juventus",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "KNB-9D6M",
+ "symbol": "KNB",
+ "name": "Kronobit",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "POLO-E0DM",
+ "symbol": "POLO",
+ "name": "MarcoPoloCoin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "FIO-0CDM",
+ "symbol": "FIO",
+ "name": "FIO",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BIR",
+ "symbol": "BIR",
+ "name": "BinanceIran",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "SBC-AF0M",
+ "symbol": "SBC",
+ "name": "SBCOIN",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "DPSC-CCBM",
+ "symbol": "DPSC",
+ "name": "DPASSCOIN",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "EPIC-066M",
+ "symbol": "EPIC",
+ "name": "EPIC",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "UBETS-619M",
+ "symbol": "UBETS",
+ "name": "UBETS Coin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "JDXU-932M",
+ "symbol": "JDXU",
+ "name": "JDXUCoin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "GMNG-084M",
+ "symbol": "GMNG",
+ "name": "GlobalGaming",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BTC-992M",
+ "symbol": "BTC",
+ "name": "Bitcoin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "ETH-EDDM",
+ "symbol": "ETH",
+ "name": "Ethereum",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "ANK-5D8M",
+ "symbol": "ANK",
+ "name": "AppleNetwork",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "FLGHT-96EM",
+ "symbol": "FLGHT",
+ "name": "TheFlight",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "YTC-6BFM",
+ "symbol": "YTC",
+ "name": "YouTubeCoin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "MSV-114M",
+ "symbol": "MSV",
+ "name": "MoskvinInvest",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "AZBIT-A07M",
+ "symbol": "AZBIT",
+ "name": "AZbitCryptoExchange",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BFT-321M",
+ "symbol": "BFT",
+ "name": "BitForexToken",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "LIBRA-D54M",
+ "symbol": "LIBRA",
+ "name": "FacebookCoin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "ZANO-F71M",
+ "symbol": "ZANO",
+ "name": "Zano",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BTS-FA7M",
+ "symbol": "BTS",
+ "name": "BTSentertainment",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "VNB-684M",
+ "symbol": "VNB",
+ "name": "VNNetwork",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "MKR-119M",
+ "symbol": "MKR",
+ "name": "Maker",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "XAV-3C5M",
+ "symbol": "XAV",
+ "name": "XAVIERCOIN",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "TSL-751M",
+ "symbol": "TSL",
+ "name": "TESLA",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "GRAM-6BBM",
+ "symbol": "GRAM",
+ "name": "TelegramOpenNetwork",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "ZENK-8BCM",
+ "symbol": "ZENK",
+ "name": "ZenKoin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "STK70-882M",
+ "symbol": "STK70",
+ "name": "STK70",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "UBER-5D0M",
+ "symbol": "UBER",
+ "name": "UBER",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "CR7-A67M",
+ "symbol": "CR7",
+ "name": "CristianoRonaldoFanToken",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "ITTT-C65M",
+ "symbol": "ITTT",
+ "name": "IntuitiveToken",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "FNXS-983M",
+ "symbol": "FNXS",
+ "name": "FinanceX",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "ELT-61FM",
+ "symbol": "ELT",
+ "name": "ELITE",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "KODK-9ABM",
+ "symbol": "KODK",
+ "name": "EastmanKodakCompany",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "COMP-3C5M",
+ "symbol": "COMP",
+ "name": "Compound",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "CCT-AD7M",
+ "symbol": "CCT",
+ "name": "CryptoCompareToken",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BNS-5ADM",
+ "symbol": "BNS",
+ "name": "BNSAFEER",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "10XDF-9FBM",
+ "symbol": "10XDF",
+ "name": "10xDeFiXy",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "RUBY-FFCM",
+ "symbol": "RUBY",
+ "name": "VNOvernight",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BHC-3E8M",
+ "symbol": "BHC",
+ "name": "BillionHappiness",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "SAND-25CM",
+ "symbol": "SAND",
+ "name": "Sandbox",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "VNBIT-5D5M",
+ "symbol": "VNBIT",
+ "name": "VNBit",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "CVD-940M",
+ "symbol": "CVD",
+ "name": "Covid19",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "100X-0A2M",
+ "symbol": "100X",
+ "name": "100x",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "PLV-328M",
+ "symbol": "PLV",
+ "name": "PLAVNI",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "VOTE-692M",
+ "symbol": "VOTE",
+ "name": "Vote",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "NBTC-09BM",
+ "symbol": "NBTC",
+ "name": "NBTC",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "WEBIT-4D0M",
+ "symbol": "WEBIT",
+ "name": "WEBIT",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BUSY-3AFM",
+ "symbol": "BUSY",
+ "name": "BusyProtocol",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "DEFIX-46FM",
+ "symbol": "DEFIX",
+ "name": "DefiX",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "200DX-985M",
+ "symbol": "200DX",
+ "name": "200DX",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "GOGH-510M",
+ "symbol": "GOGH",
+ "name": "Vincent",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "HOOTZ-CE2M",
+ "symbol": "HOOTZ",
+ "name": "Hootz",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "YFFI-7E4M",
+ "symbol": "YFFI",
+ "name": "YFFIfinance",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "YFII-7FBM",
+ "symbol": "YFII",
+ "name": "yearnfinanceII",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "TNH-2D6M",
+ "symbol": "TNH",
+ "name": "Tanhu",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "GOLD-42AM",
+ "symbol": "GOLD",
+ "name": "GOLD",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "CMW-CF8M",
+ "symbol": "CMW",
+ "name": "CMWLTD14",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "AMZN-2A5M",
+ "symbol": "AMZN",
+ "name": "Amazon",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "YFL-4CFM",
+ "symbol": "YFL",
+ "name": "YFLink",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "CUCAP-090M",
+ "symbol": "CUCAP",
+ "name": "CopperCommons",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "YETH-687M",
+ "symbol": "YETH",
+ "name": "YETH",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "XLS-A82M",
+ "symbol": "XLS",
+ "name": "Xwolfers",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "POKER-C92M",
+ "symbol": "POKER",
+ "name": "POKERSTARS",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "XCT",
+ "symbol": "XCT",
+ "name": "Citron",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "SIP",
+ "symbol": "SIP",
+ "name": "Arbitrx",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "KDC-4E4M",
+ "symbol": "KDC",
+ "name": "KingdomCasino",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "INNBC",
+ "symbol": "INNBC",
+ "name": "InnovativeBioresearchCoin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "ODAEE-AD8M",
+ "symbol": "ODAEE",
+ "name": "ODAEE",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "CALTO-C51M",
+ "symbol": "CALTO",
+ "name": "caltechcasino",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "ZONIC-C26M",
+ "symbol": "ZONIC",
+ "name": "ZONIC",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "TAINO-CC7M",
+ "symbol": "TAINO",
+ "name": "TAINOCOIN",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BZAR-188M",
+ "symbol": "BZAR",
+ "name": "Bitcoinrand",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "JSB-614M",
+ "symbol": "JSB",
+ "name": "JSB",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BSATT-9F8M",
+ "symbol": "BSATT",
+ "name": "BinanceSATT",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "GOAL-A7EM",
+ "symbol": "GOAL",
+ "name": "GoalCoin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "ALF-346M",
+ "symbol": "ALF",
+ "name": "AlfaBlock",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "GLXY-783M",
+ "symbol": "GLXY",
+ "name": "GalaxianCoin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "FXP-BE7M",
+ "symbol": "FXP",
+ "name": "FXPay",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "ATM-95EM",
+ "symbol": "ATM",
+ "name": "Atletico Madrid",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "VRTN2-F89M",
+ "symbol": "VRTN2",
+ "name": "VINYL RECORDS TOKEN 2",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "STN-DB6M",
+ "symbol": "STN",
+ "name": "Saturno",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "TON-12EM",
+ "symbol": "TON",
+ "name": "Telegram Open Network",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "FLATS-9A4M",
+ "symbol": "FLATS",
+ "name": "FLAT EARTH",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "LTT-D42M",
+ "symbol": "LTT",
+ "name": "LiteTrader",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "FUTSWAP-8D1M",
+ "symbol": "FUTSWAP",
+ "name": "Futswap",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "IBAI-6FDM",
+ "symbol": "IBAI",
+ "name": "IBAI FAN TOKEN",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BNGO-9D3M",
+ "symbol": "BNGO",
+ "name": "BINGO",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "STH-07AM",
+ "symbol": "STH",
+ "name": "SmartHoldem",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "WSB-63CM",
+ "symbol": "WSB",
+ "name": "WallStreetBets",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "XAQ-ABCM",
+ "symbol": "XAQ",
+ "name": "AQAQ",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "XCL-DAFM",
+ "symbol": "XCL",
+ "name": "CLICO",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "AGU-14FM",
+ "symbol": "AGU",
+ "name": "Aguacate Mini Token",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "GRPX-D25M",
+ "symbol": "GRPX",
+ "name": "Grapbox",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "SHIT-580M",
+ "symbol": "SHIT",
+ "name": "SHIT DIAMOND",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "SWAN-288M",
+ "symbol": "SWAN",
+ "name": "Swan Finance",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "TRS-182M",
+ "symbol": "TRS",
+ "name": "Traders",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "SZR-4F6M",
+ "symbol": "SZR",
+ "name": "Smart ZAR",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "TOD-442M",
+ "symbol": "TOD",
+ "name": "TOD COIN",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "WWWC-C3EM",
+ "symbol": "WWWC",
+ "name": "World Wide Wallet Coin",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "CFN-D88M",
+ "symbol": "CFN",
+ "name": "Cafeina Token",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "BZB-034M",
+ "symbol": "BZB",
+ "name": "One million BZE",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "CCSCEX-E2CM",
+ "symbol": "CCSCEX",
+ "name": "EXCHANGE",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "XOTL-247M",
+ "symbol": "XOTL",
+ "name": "Xolotl Token",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "UWIM-06AM",
+ "symbol": "UWIM",
+ "name": "UWIM",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "LEARN",
+ "symbol": "LEARN",
+ "name": "BinanceAcademyToken",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "IKA-105M",
+ "symbol": "IKA",
+ "name": "Mining",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "contractAddress": "SCAM-914M",
+ "symbol": "SCAM",
+ "name": "SCAM",
+ "decimalCount": 8,
+ "type": "bep8",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ }
+]
diff --git a/app/src/main/assets/binance_tokens_testnet.json b/app/src/main/assets/binance_tokens_testnet.json
new file mode 100644
index 0000000000..7d5590d293
--- /dev/null
+++ b/app/src/main/assets/binance_tokens_testnet.json
@@ -0,0 +1,14 @@
+[
+ {
+ "symbol" : "HEM",
+ "decimalCount" : 8,
+ "contractAddress" : "HEM-452",
+ "blockchain" : {
+ "key" : "binance",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/binance/assets/MATIC-84A/logo.png",
+ "name" : "Hemster - 452"
+ }
+]
diff --git a/app/src/main/assets/bsc_tokens.json b/app/src/main/assets/bsc_tokens.json
new file mode 100644
index 0000000000..712d35c6dd
--- /dev/null
+++ b/app/src/main/assets/bsc_tokens.json
@@ -0,0 +1,782 @@
+[
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x6c5FE6e99D2484db7E4BF34F365ABA42d0E4dC20/logo.png",
+ "symbol" : "ABS",
+ "name" : "Absorber",
+ "contractAddress" : "0x6c5FE6e99D2484db7E4BF34F365ABA42d0E4dC20",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x4197C6EF3879a08cD51e5560da5064B773aa1d29/logo.png",
+ "symbol" : "ACS",
+ "name" : "ACryptoS",
+ "contractAddress" : "0x4197C6EF3879a08cD51e5560da5064B773aa1d29",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xa1faa113cbE53436Df28FF0aEe54275c13B40975/logo.png",
+ "symbol" : "ALPHA",
+ "name" : "Alpha Token",
+ "contractAddress" : "0xa1faa113cbE53436Df28FF0aEe54275c13B40975",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xa184088a740c695E156F91f5cC086a06bb78b827/logo.png",
+ "symbol" : "AUTO",
+ "name" : "AUTOv2",
+ "contractAddress" : "0xa184088a740c695E156F91f5cC086a06bb78b827",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56/logo.png",
+ "symbol" : "BUSD",
+ "name" : "BUSD",
+ "contractAddress" : "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xAD6cAEb32CD2c308980a548bD0Bc5AA4306c6c18/logo.png",
+ "symbol" : "BAND",
+ "name" : "Binance-Peg Band Protocol",
+ "contractAddress" : "0xAD6cAEb32CD2c308980a548bD0Bc5AA4306c6c18",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x8fF795a6F4D97E7887C79beA79aba5cc76444aDf/logo.png",
+ "symbol" : "BCH",
+ "name" : "Binance-Peg Bitcoin Cash",
+ "contractAddress" : "0x8fF795a6F4D97E7887C79beA79aba5cc76444aDf",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x72fAa679E1008Ad8382959FF48E392042A8b06f7/logo.png",
+ "symbol" : "bALBT",
+ "name" : "AllianceBlock Token",
+ "contractAddress" : "0x72fAa679E1008Ad8382959FF48E392042A8b06f7",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x63870A18B6e42b01Ef1Ad8A2302ef50B7132054F/logo.png",
+ "symbol" : "BLINK",
+ "name" : "BLink Token",
+ "contractAddress" : "0x63870A18B6e42b01Ef1Ad8A2302ef50B7132054F",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x5Ac52EE5b2a633895292Ff6d8A89bB9190451587/logo.png",
+ "symbol" : "BSCX",
+ "name" : "BSCX",
+ "contractAddress" : "0x5Ac52EE5b2a633895292Ff6d8A89bB9190451587",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c/logo.png",
+ "symbol" : "BTCB",
+ "name" : "Binance-Peg BTCB Token",
+ "contractAddress" : "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47/logo.png",
+ "symbol" : "ADA",
+ "name" : "Binance-Peg Cardano",
+ "contractAddress" : "0x3EE2200Efb3400fAbB9AacF31297cBdD1d435D47",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD/logo.png",
+ "symbol" : "LINK",
+ "name" : "Binance-Peg ChainLink",
+ "contractAddress" : "0xF8A0BF9cF54Bb92F17374d9e9A321E6a111a51bD",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x0Eb3a705fc54725037CC9e008bDede697f62F335/logo.png",
+ "symbol" : "ATOM",
+ "name" : "Binance-Peg Cosmos",
+ "contractAddress" : "0x0Eb3a705fc54725037CC9e008bDede697f62F335",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51/logo.png",
+ "symbol" : "BUNNY",
+ "name" : "Bunny Token",
+ "contractAddress" : "0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xBf5140A22578168FD562DCcF235E5D43A02ce9B1/logo.png",
+ "symbol" : "UNI",
+ "name" : "Binance-Peg Uniswap",
+ "contractAddress" : "0xBf5140A22578168FD562DCcF235E5D43A02ce9B1",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x88f1A5ae2A3BF98AEAF342D26B30a79438c9142e/logo.png",
+ "symbol" : "YFI",
+ "name" : "Binance-Peg yearn.finance",
+ "contractAddress" : "0x88f1A5ae2A3BF98AEAF342D26B30a79438c9142e",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x81859801b01764D4f0Fa5E64729f5a6C3b91435b/logo.png",
+ "symbol" : "BFI",
+ "name" : "bearn.fi",
+ "contractAddress" : "0x81859801b01764D4f0Fa5E64729f5a6C3b91435b",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xCa3F508B8e4Dd382eE878A314789373D80A5190A/logo.png",
+ "symbol" : "BIFI",
+ "name" : "beefy.finance",
+ "contractAddress" : "0xCa3F508B8e4Dd382eE878A314789373D80A5190A",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7F70642d88cf1C4a3a7abb072B53B929b653edA5/logo.png",
+ "symbol" : "YFII",
+ "name" : "Binance-Peg YFII.finance",
+ "contractAddress" : "0x7F70642d88cf1C4a3a7abb072B53B929b653edA5",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x790Be81C3cA0e53974bE2688cDb954732C9862e1/logo.png",
+ "symbol" : "BREW",
+ "name" : "CafeSwap Token",
+ "contractAddress" : "0x790Be81C3cA0e53974bE2688cDb954732C9862e1",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xcc2E12a9b5b75360c6FBf23B584c275D52cDdb0E/logo.png",
+ "symbol" : "CROW",
+ "name" : "Crow Token",
+ "contractAddress" : "0xcc2E12a9b5b75360c6FBf23B584c275D52cDdb0E",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xA8c2B8eec3d368C0253ad3dae65a5F2BBB89c929/logo.png",
+ "symbol" : "CTK",
+ "name" : "CertiK Token",
+ "contractAddress" : "0xA8c2B8eec3d368C0253ad3dae65a5F2BBB89c929",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x97a30C692eCe9C317235d48287d23d358170FC40/logo.png",
+ "symbol" : "CRX",
+ "name" : "CryptEx Token",
+ "contractAddress" : "0x97a30C692eCe9C317235d48287d23d358170FC40",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xF952Fc3ca7325Cc27D15885d37117676d25BfdA6/logo.png",
+ "symbol" : "EGG",
+ "name" : "Goose Golden Egg",
+ "contractAddress" : "0xF952Fc3ca7325Cc27D15885d37117676d25BfdA6",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xbF7c81FFF98BbE61B40Ed186e4AfD6DDd01337fe/logo.png",
+ "symbol" : "EGLD",
+ "name" : "Elrond",
+ "contractAddress" : "0xbF7c81FFF98BbE61B40Ed186e4AfD6DDd01337fe",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x928e55daB735aa8260AF3cEDadA18B5f70C72f1b/logo.png",
+ "symbol" : "FRONT",
+ "name" : "Frontier Token",
+ "contractAddress" : "0x928e55daB735aa8260AF3cEDadA18B5f70C72f1b",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xf79037F6f6bE66832DE4E7516be52826BC3cBcc4/logo.png",
+ "symbol" : "HARD",
+ "name" : "Hard Protocol",
+ "contractAddress" : "0xf79037F6f6bE66832DE4E7516be52826BC3cBcc4",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xC7d8D35EBA58a0935ff2D5a33Df105DD9f071731/logo.png",
+ "symbol" : "HGET",
+ "name" : "Hedget",
+ "contractAddress" : "0xC7d8D35EBA58a0935ff2D5a33Df105DD9f071731",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x948d2a81086A075b3130BAc19e4c6DEe1D2E3fE8/logo.png",
+ "symbol" : "Helmet",
+ "name" : "Helmet.insure Governance Token",
+ "contractAddress" : "0x948d2a81086A075b3130BAc19e4c6DEe1D2E3fE8",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xa2B726B1145A4773F68593CF171187d8EBe4d495/logo.png",
+ "symbol" : "INJ",
+ "name" : "Injective Protocol",
+ "contractAddress" : "0xa2B726B1145A4773F68593CF171187d8EBe4d495",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7979F6C54ebA05E18Ded44C4F986F49a5De551c2/logo.png",
+ "symbol" : "KEBAB",
+ "name" : "Kebab Token",
+ "contractAddress" : "0x7979F6C54ebA05E18Ded44C4F986F49a5De551c2",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xb59490aB09A0f526Cc7305822aC65f2Ab12f9723/logo.png",
+ "symbol" : "LIT",
+ "name" : "Litentry",
+ "contractAddress" : "0xb59490aB09A0f526Cc7305822aC65f2Ab12f9723",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png",
+ "symbol" : "MATIC",
+ "name" : "Matic Token",
+ "contractAddress" : "0xcc42724c6683b7e57334c4e856f4c9965ed682bd",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xC7FB8c5eFE8564205eE5CbA4dE8eeA8a086a4bd2/logo.png",
+ "symbol" : "POCROC",
+ "name" : "PocketRocket",
+ "contractAddress" : "0xC7FB8c5eFE8564205eE5CbA4dE8eeA8a086a4bd2",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xF21768cCBC73Ea5B6fd3C687208a7c2def2d966e/logo.png",
+ "symbol" : "REEF",
+ "name" : "Reef.finance",
+ "contractAddress" : "0xF21768cCBC73Ea5B6fd3C687208a7c2def2d966e",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xE40255C5d7fa7ceEc5120408C78C787CECB4cfdb/logo.png",
+ "symbol" : "SWGb",
+ "name" : "SWGb",
+ "contractAddress" : "0xE40255C5d7fa7ceEc5120408C78C787CECB4cfdb",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xD41FDb03Ba84762dD66a0af1a6C8540FF1ba5dfb/logo.png",
+ "symbol" : "SFP",
+ "name" : "SafePal Token",
+ "contractAddress" : "0xD41FDb03Ba84762dD66a0af1a6C8540FF1ba5dfb",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x78650B139471520656b9E7aA7A5e9276814a38e9/logo.png",
+ "symbol" : "BTCST",
+ "name" : "StandardBTCHashrateToken",
+ "contractAddress" : "0x78650B139471520656b9E7aA7A5e9276814a38e9",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3/logo.png",
+ "symbol" : "DAI",
+ "name" : "Binance-Peg Dai Token",
+ "contractAddress" : "0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7083609fCE4d1d8Dc0C979AAb8c869Ea2C873402/logo.png",
+ "symbol" : "DOT",
+ "name" : "Binance-Peg Polkadot",
+ "contractAddress" : "0x7083609fCE4d1d8Dc0C979AAb8c869Ea2C873402",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x67ee3Cb086F8a16f34beE3ca72FAD36F7Db929e2/logo.png",
+ "symbol" : "DODO",
+ "name" : "DODO",
+ "contractAddress" : "0x67ee3Cb086F8a16f34beE3ca72FAD36F7Db929e2",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x56b6fB708fC5732DEC1Afc8D8556423A2EDcCbD6/logo.png",
+ "symbol" : "EOS",
+ "name" : "Binance-Peg EOS",
+ "contractAddress" : "0x56b6fB708fC5732DEC1Afc8D8556423A2EDcCbD6",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x233d91A0713155003fc4DcE0AFa871b508B3B715/logo.png",
+ "symbol" : "DITTO",
+ "name" : "Ditto",
+ "contractAddress" : "0x233d91A0713155003fc4DcE0AFa871b508B3B715",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x2170Ed0880ac9A755fd29B2688956BD959F933F8/logo.png",
+ "symbol" : "ETH",
+ "name" : "Ethereum Token",
+ "contractAddress" : "0x2170Ed0880ac9A755fd29B2688956BD959F933F8",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x190b589cf9Fb8DDEabBFeae36a813FFb2A702454/logo.png",
+ "symbol" : "BDO",
+ "name" : "bDollar",
+ "contractAddress" : "0x190b589cf9Fb8DDEabBFeae36a813FFb2A702454",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82/logo.png",
+ "symbol" : "Cake",
+ "name" : "PancakeSwap Token",
+ "contractAddress" : "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x0D8Ce2A99Bb6e3B7Db580eD848240e4a0F9aE153/logo.png",
+ "symbol" : "FIL",
+ "name" : "Binance-Peg Filecoin",
+ "contractAddress" : "0x0D8Ce2A99Bb6e3B7Db580eD848240e4a0F9aE153",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x4338665CBB7B2485A8855A139b75D5e34AB0DB94/logo.png",
+ "symbol" : "LTC",
+ "name" : "Binance-Peg Litecoin",
+ "contractAddress" : "0x4338665CBB7B2485A8855A139b75D5e34AB0DB94",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x55d398326f99059fF775485246999027B3197955/logo.png",
+ "symbol" : "USDT",
+ "name" : "Binance-Peg Tether USD",
+ "contractAddress" : "0x55d398326f99059fF775485246999027B3197955",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x2090c8295769791ab7A3CF1CC6e0AA19F35e441A/logo.png",
+ "symbol" : "Fuel",
+ "name" : "Fuel Token",
+ "contractAddress" : "0x2090c8295769791ab7A3CF1CC6e0AA19F35e441A",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x3aabCf53A1930A42E18D938C019E83Ebee50a849/logo.png",
+ "symbol" : "SPG",
+ "name" : "SPONGE",
+ "contractAddress" : "0x3aabCf53A1930A42E18D938C019E83Ebee50a849",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x37Ac4D6140e54304D77437A5c11924f61a2D976f/logo.png",
+ "symbol" : "SFUEL",
+ "name" : "SparkPoint Fuel",
+ "contractAddress" : "0x37Ac4D6140e54304D77437A5c11924f61a2D976f",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x47BEAd2563dCBf3bF2c9407fEa4dC236fAbA485A/logo.png",
+ "symbol" : "SXP",
+ "name" : "Swipe",
+ "contractAddress" : "0x47BEAd2563dCBf3bF2c9407fEa4dC236fAbA485A",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xeAF7D8395CCE52DAef138d39a1CEfA51b97C15aE/logo.png",
+ "symbol" : "TBC",
+ "name" : "TeraBlock Token",
+ "contractAddress" : "0xeAF7D8395CCE52DAef138d39a1CEfA51b97C15aE",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x4B0F1812e5Df2A09796481Ff14017e6005508003/logo.png",
+ "symbol" : "TWT",
+ "name" : "Trust Wallet",
+ "contractAddress" : "0x4B0F1812e5Df2A09796481Ff14017e6005508003",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x728C5baC3C3e370E372Fc4671f9ef6916b814d8B/logo.png",
+ "symbol" : "UNFI",
+ "name" : "UNFI",
+ "contractAddress" : "0x728C5baC3C3e370E372Fc4671f9ef6916b814d8B",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x4BD17003473389A42DAF6a0a729f6Fdb328BbBd7/logo.png",
+ "symbol" : "VAI",
+ "name" : "VAI Stablecoin",
+ "contractAddress" : "0x4BD17003473389A42DAF6a0a729f6Fdb328BbBd7",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x23396cF899Ca06c4472205fC903bDB4de249D6fC/logo.png",
+ "symbol" : "UST",
+ "name" : "Wrapped UST Token",
+ "contractAddress" : "0x23396cF899Ca06c4472205fC903bDB4de249D6fC",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE/logo.png",
+ "symbol" : "XRP",
+ "name" : "Binance-Peg XRP",
+ "contractAddress" : "0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x16939ef78684453bfDFb47825F8a5F714f12623a/logo.png",
+ "symbol" : "XTZ",
+ "name" : "Binance-Peg Tezos",
+ "contractAddress" : "0x16939ef78684453bfDFb47825F8a5F714f12623a",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xcF6BB5389c92Bdda8a3747Ddb454cB7a64626C63/logo.png",
+ "symbol" : "XVS",
+ "name" : "Venus",
+ "contractAddress" : "0xcF6BB5389c92Bdda8a3747Ddb454cB7a64626C63",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x0231f91e02DebD20345Ae8AB7D71A41f8E140cE7/logo.png",
+ "symbol" : "bwJUP",
+ "name" : "Jupiter",
+ "contractAddress" : "0x0231f91e02DebD20345Ae8AB7D71A41f8E140cE7",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c/logo.png",
+ "symbol" : "WBNB",
+ "name" : "Wrapped BNB",
+ "contractAddress" : "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x44754455564474A89358B2C2265883DF993b12F0/logo.png",
+ "symbol" : "ZEE",
+ "name" : "ZeroSwapToken",
+ "contractAddress" : "0x44754455564474A89358B2C2265883DF993b12F0",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ }
+]
diff --git a/app/src/main/assets/bsc_tokens_testnet.json b/app/src/main/assets/bsc_tokens_testnet.json
new file mode 100644
index 0000000000..c07946b0d7
--- /dev/null
+++ b/app/src/main/assets/bsc_tokens_testnet.json
@@ -0,0 +1,99 @@
+[
+ {
+ "decimalCount" : 18,
+ "symbol" : "BUSD",
+ "name" : "Binance-Peg BUSD Token",
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56/logo.png",
+ "contractAddress" : "0xed24fc36d5ee211ea25a80239fb8c4cfd80f12ee",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "ETH",
+ "name" : "Ethereum Token",
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x2170Ed0880ac9A755fd29B2688956BD959F933F8/logo.png",
+ "contractAddress" : "0xd66c6b4f0be8ce5b39d52e0fd1344c389929b378",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "BTCB",
+ "name" : "Binance-Peg BTCB Token",
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c/logo.png",
+ "contractAddress" : "0x6ce8da28e2f864420840cf74474eff5fd80e65b8",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "USDT",
+ "name" : "Binance-Peg Tether USD",
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x55d398326f99059fF775485246999027B3197955/logo.png",
+ "contractAddress" : "0x337610d27c682e347c9cd60bd4b3b107c9d34ddd",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "USDT",
+ "name" : "Tether USD",
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x55d398326f99059fF775485246999027B3197955/logo.png",
+ "contractAddress" : "0x7ef95a0fee0dd31b22626fa2e10ee6a223f8a684",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "XRP",
+ "name" : "Binance-Peg XRP",
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE/logo.png",
+ "contractAddress" : "0xa83575490d7df4e2f47b7d38ef351a2722ca45b9",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "DAI",
+ "name" : "Binance-Peg Dai Token",
+ "customIconUrl" : "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3/logo.png",
+ "contractAddress" : "0xec5dcb5dbf4b114c9d0f65bccab49ec54f6a0867",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "USDC",
+ "name" : "Binance-Peg USDC Token",
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
+ "contractAddress" : "0x64544969ed7ebf5f083679233325356ebe738930",
+ "blockchain" : {
+ "key" : "bsc",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ }
+]
+
diff --git a/app/src/main/assets/erc20_tokens.json b/app/src/main/assets/erc20_tokens.json
deleted file mode 100644
index 22fe8bd802..0000000000
--- a/app/src/main/assets/erc20_tokens.json
+++ /dev/null
@@ -1,927 +0,0 @@
-[
- {
- "decimalCount" : 18,
- "symbol" : "ZRX",
- "name" : "0x",
- "contractAddress" : "0xE41d2489571d322189246DaFA5ebDe1F4699F498"
- },
- {
- "decimalCount" : 18,
- "symbol" : "AAVE",
- "name" : "Aave",
- "contractAddress" : "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9"
- },
- {
- "decimalCount" : 18,
- "symbol" : "LEND",
- "name" : "Aave (OLD)",
- "contractAddress" : "0x80fB784B7eD66730e8b1DBd9820aFD29931aab03"
- },
- {
- "decimalCount" : 18,
- "symbol" : "ADX",
- "name" : "AdEx",
- "contractAddress" : "0xADE00C28244d5CE17D72E40330B1c318cD12B7c3"
- },
- {
- "decimalCount" : 9,
- "symbol" : "ADT",
- "name" : "adToken",
- "contractAddress" : "0xD0D6D6C5Fe4a677D343cC433536BB717bAe167dD"
- },
- {
- "decimalCount" : 18,
- "symbol" : "ELF",
- "name" : "Aelf",
- "contractAddress" : "0xbf2179859fc6d5bee9bf9158632dc51678a4100e"
- },
- {
- "decimalCount" : 8,
- "symbol" : "ARN",
- "name" : "Aeron",
- "contractAddress" : "0xBA5F11b16B155792Cf3B2E6880E8706859A8AEB6"
- },
- {
- "decimalCount" : 18,
- "symbol" : "AE",
- "name" : "Aeternity",
- "contractAddress" : "0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d"
- },
- {
- "decimalCount" : 8,
- "symbol" : "AION",
- "name" : "Aion",
- "contractAddress" : "0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466"
- },
- {
- "decimalCount" : 4,
- "symbol" : "AST",
- "name" : "Airswap",
- "contractAddress" : "0x27054b13b1b798b345b591a4d22e6562d47ea75a"
- },
- {
- "decimalCount" : 18,
- "symbol" : "AMB",
- "name" : "Amber",
- "contractAddress" : "0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce"
- },
- {
- "decimalCount" : 18,
- "symbol" : "AMLT",
- "name" : "AMLT by Coinfirm",
- "contractAddress" : "0xca0e7269600d353f70b14ad118a49575455c0f2f"
- },
- {
- "decimalCount" : 18,
- "symbol" : "APPC",
- "name" : "AppCoins",
- "contractAddress" : "0x1a7a8bd9106f2b8d977e08582dc7d24c723ab0db"
- },
- {
- "decimalCount" : 18,
- "symbol" : "ANT",
- "name" : "Aragon",
- "contractAddress" : "0xa117000000f279d81a1d3cc75430faa017fa5a2e"
- },
- {
- "decimalCount" : 18,
- "symbol" : "ABT",
- "name" : "ArcBlock",
- "contractAddress" : "0xb98d4c97425d9908e66e53a6fdf673acca0be986"
- },
- {
- "decimalCount" : 18,
- "symbol" : "ATMI",
- "name" : "Atonomi",
- "contractAddress" : "0x97aeb5066e1a590e868b511457beb6fe99d329f5"
- },
- {
- "decimalCount" : 18,
- "symbol" : "REP",
- "name" : "Augur",
- "contractAddress" : "0x1985365e9f78359a9B6AD760e32412f4a445E862"
- },
- {
- "decimalCount" : 18,
- "symbol" : "AVM",
- "name" : "AVM Ecosystem",
- "contractAddress" : "0x74004a7227615fb52b82d17ffabfa376907d8a4d"
- },
- {
- "decimalCount" : 18,
- "symbol" : "BNT",
- "name" : "Bancor",
- "contractAddress" : "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C"
- },
- {
- "decimalCount" : 18,
- "symbol" : "BAT",
- "name" : "Basic Attention",
- "contractAddress" : "0x0D8775F648430679A709E98d2b0Cb6250d2887EF"
- },
- {
- "decimalCount" : 18,
- "symbol" : "BNB",
- "name" : "Binance Coin",
- "contractAddress" : "0xb8c77482e45f1f44de1745f52c74426c631bdd52"
- },
- {
- "decimalCount" : 18,
- "symbol" : "BUSD",
- "name" : "Binance USD",
- "contractAddress" : "0x4fabb145d64652a948d72533023f6e7a623c7c53"
- },
- {
- "decimalCount" : 18,
- "symbol" : "STU",
- "name" : "bitJob",
- "contractAddress" : "0x0371a82e4a9d0a4312f3ee2ac9c6958512891372"
- },
- {
- "decimalCount" : 18,
- "symbol" : "BIX",
- "name" : "BIX Token",
- "contractAddress" : "0xb3104b4b9da82025e8b9f8fb28b3553ce2f67069"
- },
- {
- "decimalCount" : 18,
- "symbol" : "VEE",
- "name" : "BLOCKv",
- "contractAddress" : "0x340d2bde5eb28c1eed91b2f790723e3b160613b7"
- },
- {
- "decimalCount" : 18,
- "symbol" : "BLZ",
- "name" : "Bluzelle",
- "contractAddress" : "0x5732046a883704404f284ce41ffadd5b007fd668"
- },
- {
- "decimalCount" : 18,
- "symbol" : "BRD",
- "name" : "BRD Token",
- "contractAddress" : "0x558ec3152e2eb2174905cd19aea4e34a23de9ad6"
- },
- {
- "decimalCount" : 8,
- "symbol" : "BTM",
- "name" : "Bytom",
- "contractAddress" : "0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750"
- },
- {
- "decimalCount" : 18,
- "symbol" : "CAS",
- "name" : "Cashaa",
- "contractAddress" : "0xe8780b48bdb05f928697a5e8155f672ed91462f7"
- },
- {
- "decimalCount" : 18,
- "symbol" : "CSP",
- "name" : "Caspian",
- "contractAddress" : "0xa6446d655a0c34bc4f05042ee88170d056cbaf45"
- },
- {
- "decimalCount" : 18,
- "symbol" : "LINK",
- "name" : "ChainLink",
- "contractAddress" : "0x514910771af9ca656af840dff83e8264ecf986ca"
- },
- {
- "decimalCount" : 8,
- "symbol" : "TIME",
- "name" : "Chronobank",
- "contractAddress" : "0x6531f133e6DeeBe7F2dcE5A0441aA7ef330B4e53"
- },
- {
- "decimalCount" : 18,
- "symbol" : "CND",
- "name" : "Cindicator",
- "contractAddress" : "0xd4c435f5b09f855c3317c8524cb1f586e42795fa"
- },
- {
- "decimalCount" : 8,
- "symbol" : "CVC",
- "name" : "Civic",
- "contractAddress" : "0x41e5560054824ea6b0732e656e3ad64e20e94e45"
- },
- {
- "decimalCount" : 18,
- "symbol" : "CNN",
- "name" : "CNN Token",
- "contractAddress" : "0x8713d26637cf49e1b6b4a7ce57106aabc9325343"
- },
- {
- "decimalCount" : 18,
- "symbol" : "CFI",
- "name" : "Cofound.it",
- "contractAddress" : "0x12FEF5e57bF45873Cd9B62E9DBd7BFb99e32D73e"
- },
- {
- "decimalCount" : 18,
- "symbol" : "CCC",
- "name" : "Container Crypto Coin",
- "contractAddress" : "0x9e3359f862b6c7f5c660cfd6d1aa6909b1d9504d"
- },
- {
- "decimalCount" : 6,
- "symbol" : "CS",
- "name" : "Credits",
- "contractAddress" : "0x46b9ad944d1059450da1163511069c718f699d31"
- },
- {
- "decimalCount" : 18,
- "symbol" : "CRPT",
- "name" : "Crypterium",
- "contractAddress" : "0x08389495d7456e1951ddf7c3a1314a4bfb646d8b"
- },
- {
- "decimalCount" : 18,
- "symbol" : "AUTO",
- "name" : "CUBE",
- "contractAddress" : "0x622dFfCc4e83C64ba959530A5a5580687a57581b"
- },
- {
- "decimalCount" : 18,
- "symbol" : "CMT",
- "name" : "CyberMiles",
- "contractAddress" : "0xf85fEea2FdD81d51177F6b8F35F0e6734Ce45F5F"
- },
- {
- "decimalCount" : 18,
- "symbol" : "DAI",
- "name" : "Dai",
- "contractAddress" : "0x6b175474e89094c44da98b954eedeac495271d0f"
- },
- {
- "decimalCount" : 18,
- "symbol" : "MANA",
- "name" : "Decentraland",
- "contractAddress" : "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942"
- },
- {
- "decimalCount" : 8,
- "symbol" : "DENT",
- "name" : "DENT",
- "contractAddress" : "0x3597bfd533a99c9aa083587b074434e61eb0a258"
- },
- {
- "decimalCount" : 0,
- "symbol" : "XJP",
- "name" : "Digital JPY",
- "contractAddress" : "0x39689fE671C01fcE173395f6BC45D4C332026666"
- },
- {
- "decimalCount" : 9,
- "symbol" : "DGD",
- "name" : "DigixDAO",
- "contractAddress" : "0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A"
- },
- {
- "decimalCount" : 18,
- "symbol" : "DNT",
- "name" : "district0x",
- "contractAddress" : "0x0abdace70d3790235af448c88547603b945604ea"
- },
- {
- "decimalCount" : 18,
- "symbol" : "DOV",
- "name" : "Dovu",
- "contractAddress" : "0xac3211a5025414af2866ff09c23fc18bc97e79b1"
- },
- {
- "decimalCount" : 18,
- "symbol" : "DRGN",
- "name" : "Dragon",
- "contractAddress" : "0x419c4db4b9e25d6db2ad9691ccb832c8d9fda05e"
- },
- {
- "decimalCount" : 0,
- "symbol" : "EDG",
- "name" : "Edgeless",
- "contractAddress" : "0x08711D3B02C8758F2FB3ab4e80228418a7F8e39c"
- },
- {
- "decimalCount" : 18,
- "symbol" : "EDO",
- "name" : "Eidoo",
- "contractAddress" : "0xced4e93198734ddaff8492d525bd258d49eb388e"
- },
- {
- "decimalCount" : 8,
- "symbol" : "ENG",
- "name" : "Enigma",
- "contractAddress" : "0xf0ee6b27b759c9893ce4f094b49ad28fd15a23e4"
- },
- {
- "decimalCount" : 18,
- "symbol" : "ENJ",
- "name" : "EnjinCoin",
- "contractAddress" : "0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c"
- },
- {
- "decimalCount" : 18,
- "symbol" : "EOS",
- "name" : "EOS",
- "contractAddress" : "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0"
- },
- {
- "decimalCount" : 2,
- "symbol" : "EUR.AVM",
- "name" : "EUR AVM",
- "contractAddress" : "0xF01Cd2f1c9E42c509d309aC8C5b29B6dA8E64b1a"
- },
- {
- "decimalCount" : 18,
- "symbol" : "XUC",
- "name" : "Exchange Union",
- "contractAddress" : "0xc324a2f6b05880503444451b8b27e6f9e63287cb"
- },
- {
- "decimalCount" : 18,
- "symbol" : "1ST",
- "name" : "FirstBlood",
- "contractAddress" : "0xAf30D2a7E90d7DC361c8C4585e9BB7D2F6f15bc7"
- },
- {
- "decimalCount" : 18,
- "symbol" : "FXC",
- "name" : "Flexacoin",
- "contractAddress" : "0x4a57E687b9126435a9B19E4A802113e266AdeBde"
- },
- {
- "decimalCount" : 8,
- "symbol" : "FUN",
- "name" : "FunFair",
- "contractAddress" : "0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b"
- },
- {
- "decimalCount" : 18,
- "symbol" : "FSN",
- "name" : "Fusion",
- "contractAddress" : "0xd0352a019e9ab9d757776f532377aaebd36fd541"
- },
- {
- "decimalCount" : 2,
- "symbol" : "GUSD",
- "name" : "Gemini Dollar",
- "contractAddress" : "0x056fd409e1d7a124bd7017459dfea2f387b6d5cd"
- },
- {
- "decimalCount" : 9,
- "symbol" : "GNX",
- "name" : "Genaro Network",
- "contractAddress" : "0x6ec8a24cabdc339a06a172f8223ea557055adaa5"
- },
- {
- "decimalCount" : 18,
- "symbol" : "GVT",
- "name" : "Genesis Vision",
- "contractAddress" : "0x103c3A209da59d3E7C4A89307e66521e081CFDF0"
- },
- {
- "decimalCount" : 5,
- "symbol" : "GTO",
- "name" : "Gifto",
- "contractAddress" : "0xc5bbae50781be1669306b9e001eff57a2957b09d"
- },
- {
- "decimalCount" : 8,
- "symbol" : "GBX",
- "name" : "Globitex",
- "contractAddress" : "0x12fcd6463e66974cf7bbc24ffc4d40d6be458283"
- },
- {
- "decimalCount" : 18,
- "symbol" : "GNO",
- "name" : "Gnosis",
- "contractAddress" : "0x6810e776880C02933D47DB1b9fc05908e5386b96"
- },
- {
- "decimalCount" : 18,
- "symbol" : "GNT",
- "name" : "Golem",
- "contractAddress" : "0xa74476443119A942dE498590Fe1f2454d7D4aC0d"
- },
- {
- "decimalCount" : 18,
- "symbol" : "HPB",
- "name" : "HPBCoin",
- "contractAddress" : "0x38c6a68304cdefb9bec48bbfaaba5c5b47818bb2"
- },
- {
- "decimalCount" : 18,
- "symbol" : "HYDRO",
- "name" : "Hydro",
- "contractAddress" : "0xebbdf302c940c6bfd49c6b165f457fdb324649bc"
- },
- {
- "decimalCount" : 18,
- "symbol" : "ICX",
- "name" : "ICON",
- "contractAddress" : "0xb5a5f22694352c15b00323844ad545abb2b11028"
- },
- {
- "decimalCount" : 18,
- "symbol" : "ICN",
- "name" : "ICONOMI",
- "contractAddress" : "0x888666CA69E0f178DED6D75b5726Cee99A87D698"
- },
- {
- "decimalCount" : 9,
- "symbol" : "RLC",
- "name" : "iExec RLC",
- "contractAddress" : "0x607F4C5BB672230e8672085532f7e901544a7375"
- },
- {
- "decimalCount" : 18,
- "symbol" : "IOST",
- "name" : "IOStoken",
- "contractAddress" : "0xfa1a856cfa3409cfa145fa4e20eb270df3eb21ab"
- },
- {
- "decimalCount" : 18,
- "symbol" : "ITC",
- "name" : "IOT Chain",
- "contractAddress" : "0x5e6b6d9abad9093fdc861ea1600eba1b355cd940"
- },
- {
- "decimalCount" : 8,
- "symbol" : "IXT",
- "name" : "iXledger",
- "contractAddress" : "0xfca47962d45adfdfd1ab2d972315db4ce7ccf094"
- },
- {
- "decimalCount" : 18,
- "symbol" : "KIN",
- "name" : "Kin",
- "contractAddress" : "0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5"
- },
- {
- "decimalCount" : 18,
- "symbol" : "KNC",
- "name" : "Kyber Network",
- "contractAddress" : "0xdd974D5C2e2928deA5F71b9825b8b646686BD200"
- },
- {
- "decimalCount" : 18,
- "symbol" : "LOOM",
- "name" : "Loom",
- "contractAddress" : "0xa4e8c3ec456107ea67d3075bf9e3df3a75823db0"
- },
- {
- "decimalCount" : 18,
- "symbol" : "LRC",
- "name" : "Loopring",
- "contractAddress" : "0xbbbbca6a901c926f240b89eacb641d8aec7aeafd"
- },
- {
- "decimalCount" : 18,
- "symbol" : "LUN",
- "name" : "Lunyr",
- "contractAddress" : "0xfa05A73FfE78ef8f1a739473e462c54bae6567D9"
- },
- {
- "decimalCount" : 18,
- "symbol" : "MKR",
- "name" : "Maker",
- "contractAddress" : "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2"
- },
- {
- "decimalCount" : 3,
- "symbol" : "GUP",
- "name" : "Matchpool",
- "contractAddress" : "0xf7B098298f7C69Fc14610bf71d5e02c60792894C"
- },
- {
- "decimalCount" : 18,
- "symbol" : "MAN",
- "name" : "MATRIX AI Network",
- "contractAddress" : "0xe25bcec5d3801ce3a794079bf94adf1b8ccd802d"
- },
- {
- "decimalCount" : 8,
- "symbol" : "MTL",
- "name" : "Metal",
- "contractAddress" : "0xF433089366899D83a9f26A773D59ec7eCF30355e"
- },
- {
- "decimalCount" : 8,
- "symbol" : "MCO",
- "name" : "Monaco",
- "contractAddress" : "0xB63B606Ac810a52cCa15e44bB630fd42D8d1d83d"
- },
- {
- "decimalCount" : 8,
- "symbol" : "MYST",
- "name" : "Mysterium",
- "contractAddress" : "0xa645264C5603E96c3b0B078cdab68733794B0A71"
- },
- {
- "decimalCount" : 18,
- "symbol" : "NAS",
- "name" : "Nebulas",
- "contractAddress" : "0x5d65D971895Edc438f465c17DB6992698a52318D"
- },
- {
- "decimalCount" : 18,
- "symbol" : "NEXO",
- "name" : "Nexo",
- "contractAddress" : "0xb62132e35a6c13ee1ee0f84dc5d40bad8d815206"
- },
- {
- "decimalCount" : 18,
- "symbol" : "NCASH",
- "name" : "Nucleus Vision",
- "contractAddress" : "0x809826cceab68c387726af962713b64cb5cb3cca"
- },
- {
- "decimalCount" : 18,
- "symbol" : "NULS",
- "name" : "Nuls",
- "contractAddress" : "0xb91318f35bdb262e9423bc7c7c2a3a93dd93c92c"
- },
- {
- "decimalCount" : 18,
- "symbol" : "NMR",
- "name" : "Numeraire",
- "contractAddress" : "0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671"
- },
- {
- "decimalCount" : 18,
- "symbol" : "OMG",
- "name" : "OmiseGO",
- "contractAddress" : "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07"
- },
- {
- "decimalCount" : 18,
- "symbol" : "OXT",
- "name" : "Orchid",
- "contractAddress" : "0x4575f41308ec1483f3d399aa9a2826d74da13deb"
- },
- {
- "decimalCount" : 18,
- "symbol" : "OST",
- "name" : "OST",
- "contractAddress" : "0x2C4e8f2D746113d0696cE89B35F0d8bF88E0AEcA"
- },
- {
- "decimalCount" : 18,
- "symbol" : "PRL",
- "name" : "Oyster Pearl",
- "contractAddress" : "0x1844b21593262668b7248d0f57a220caaba46ab9"
- },
- {
- "decimalCount" : 8,
- "symbol" : "PTOY",
- "name" : "Patientory",
- "contractAddress" : "0x8Ae4BF2C33a8e667de34B54938B0ccD03Eb8CC06"
- },
- {
- "decimalCount" : 18,
- "symbol" : "PAX",
- "name" : "Paxos Standard",
- "contractAddress" : "0x8e870d67f660d95d5be530380d0ec0bd388289e1"
- },
- {
- "decimalCount" : 18,
- "symbol" : "PMTN",
- "name" : "Peer Mountain",
- "contractAddress" : "0xe91df2bb9bccd18c45f01843cab88768d64f2d32"
- },
- {
- "decimalCount" : 8,
- "symbol" : "POE",
- "name" : "Po.et",
- "contractAddress" : "0x0e0989b1f9b8a38983c2ba8053269ca62ec9b195"
- },
- {
- "decimalCount" : 18,
- "symbol" : "PAL",
- "name" : "PolicyPal Network",
- "contractAddress" : "0xfeDAE5642668f8636A11987Ff386bfd215F942EE"
- },
- {
- "decimalCount" : 18,
- "symbol" : "POLY",
- "name" : "Polymath",
- "contractAddress" : "0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC"
- },
- {
- "decimalCount" : 8,
- "symbol" : "PPT",
- "name" : "Populous",
- "contractAddress" : "0xd4fa1460F537bb9085d22C7bcCB5DD450Ef28e3a"
- },
- {
- "decimalCount" : 6,
- "symbol" : "POWR",
- "name" : "Power Ledger",
- "contractAddress" : "0x595832f8fc6bf59c85c527fec3740a1b7a361269"
- },
- {
- "decimalCount" : 18,
- "symbol" : "NPXS",
- "name" : "Pundi X Token",
- "contractAddress" : "0xa15c7ebe1f07caf6bff097d8a589fb8ac49ae5b3"
- },
- {
- "decimalCount" : 6,
- "symbol" : "QASH",
- "name" : "QASH",
- "contractAddress" : "0x618e75ac90b12c6049ba3b27f5d5f8651b0037f6"
- },
- {
- "decimalCount" : 8,
- "symbol" : "QCX",
- "name" : "QuickX Protocol",
- "contractAddress" : "0xf9e5af7b42d31d51677c75bbbd37c1986ec79aee",
- "customIcon": "qcx"
- },
- {
- "decimalCount" : 8,
- "symbol" : "QRL",
- "name" : "QRL",
- "contractAddress" : "0x697beac28B09E122C4332D163985e8a73121b97F"
- },
- {
- "decimalCount" : 18,
- "symbol" : "QSP",
- "name" : "Quantstamp",
- "contractAddress" : "0x99ea4dB9EE77ACD40B119BD1dC4E33e1C070b80d"
- },
- {
- "decimalCount" : 18,
- "symbol" : "RDN",
- "name" : "Raiden Network",
- "contractAddress" : "0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6"
- },
- {
- "decimalCount" : 18,
- "symbol" : "RCC",
- "name" : "Reality Clash",
- "contractAddress" : "0x9b6443b0fb9c241a7fdac375595cea13e6b7807a"
- },
- {
- "decimalCount" : 6,
- "symbol" : "RC1U03",
- "name" : "RentalCoin 1.0",
- "contractAddress" : "0x7384db1aaa06364d1e6e67f5434391d3eea2a038"
- },
- {
- "decimalCount" : 18,
- "symbol" : "REQ",
- "name" : "Request",
- "contractAddress" : "0x8f8221aFbB33998d8584A2B05749bA73c37a938a"
- },
- {
- "decimalCount" : 0,
- "symbol" : "REV",
- "name" : "Revain",
- "contractAddress" : "0x48f775efbe4f5ece6e0df2f7b5932df56823b990"
- },
- {
- "decimalCount" : 18,
- "symbol" : "RCN",
- "name" : "Ripio Credit",
- "contractAddress" : "0xf970b8e36e23f7fc3fd752eea86f8be8d83375a6"
- },
- {
- "decimalCount" : 18,
- "symbol" : "RVT",
- "name" : "Rivetz",
- "contractAddress" : "0x3d1ba9be9f66b8ee101911bc36d3fb562eac2244"
- },
- {
- "decimalCount" : 18,
- "symbol" : "RUFF",
- "name" : "RUFF",
- "contractAddress" : "0xf278c1ca969095ffddded020290cf8b5c424ace2"
- },
- {
- "decimalCount" : 18,
- "symbol" : "SAI",
- "name" : "Sai",
- "contractAddress" : "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"
- },
- {
- "decimalCount" : 8,
- "symbol" : "SALT",
- "name" : "SALT",
- "contractAddress" : "0x4156D3342D5c385a87D264F90653733592000581"
- },
- {
- "decimalCount" : 18,
- "symbol" : "SAN",
- "name" : "SAN",
- "contractAddress" : "0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098"
- },
- {
- "decimalCount" : 8,
- "symbol" : "SENT",
- "name" : "SENTinel",
- "contractAddress" : "0xa44e5137293e855b1b7bc7e2c6f8cd796ffcb037"
- },
- {
- "decimalCount" : 0,
- "symbol" : "SNGLS",
- "name" : "Singular DTV",
- "contractAddress" : "0xaeC2E87E0A235266D9C5ADc9DEb4b2E29b54D009"
- },
- {
- "decimalCount" : 8,
- "symbol" : "AGI",
- "name" : "SingularityNET",
- "contractAddress" : "0x8eB24319393716668D768dCEC29356ae9CfFe285"
- },
- {
- "decimalCount" : 18,
- "symbol" : "SRN",
- "name" : "SIRIN",
- "contractAddress" : "0x68d57c9a1C35f63E2c83eE8e49A64e9d70528D25"
- },
- {
- "decimalCount" : 18,
- "symbol" : "SNM",
- "name" : "SONM",
- "contractAddress" : "0x983F6d60db79ea8cA4eB9968C6aFf8cfA04B3c63"
- },
- {
- "decimalCount" : 18,
- "symbol" : "SNT",
- "name" : "Status",
- "contractAddress" : "0x744d70FDBE2Ba4CF95131626614a1763DF805B9E"
- },
- {
- "decimalCount" : 18,
- "symbol" : "STK",
- "name" : "STK",
- "contractAddress" : "0xae73b38d1c9a8b274127ec30160a4927c4d71824"
- },
- {
- "decimalCount" : 18,
- "symbol" : "STQ",
- "name" : "Storiqa",
- "contractAddress" : "0x5c3a228510d246b78a3765c20221cbf3082b44a4"
- },
- {
- "decimalCount" : 8,
- "symbol" : "STORJ",
- "name" : "Storj",
- "contractAddress" : "0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC"
- },
- {
- "decimalCount" : 18,
- "symbol" : "STORM",
- "name" : "Storm",
- "contractAddress" : "0xD0a4b8946Cb52f0661273bfbC6fD0E0C75Fc6433"
- },
- {
- "decimalCount" : 18,
- "symbol" : "STMX",
- "name" : "StormX",
- "contractAddress" : "0xbE9375C6a420D2eEB258962efB95551A5b722803"
- },
- {
- "decimalCount" : 18,
- "symbol" : "STX",
- "name" : "Stox",
- "contractAddress" : "0x006BeA43Baa3f7A6f765F14f10A1a1b08334EF45"
- },
- {
- "decimalCount" : 18,
- "symbol" : "SUB",
- "name" : "Substratum",
- "contractAddress" : "0x8D75959f1E61EC2571aa72798237101F084DE63a"
- },
- {
- "decimalCount" : 18,
- "symbol" : "SNC",
- "name" : "SunContract",
- "contractAddress" : "0xF4134146AF2d511Dd5EA8cDB1C4AC88C57D60404"
- },
- {
- "decimalCount" : 18,
- "symbol" : "SUSHI",
- "name" : "SushiSwap",
- "contractAddress" : "0x6b3595068778dd592e39a122f4f5a5cf09c90fe2"
- },
- {
- "decimalCount" : 2,
- "symbol" : "TEL",
- "name" : "Telcoin",
- "contractAddress" : "0x467bccd9d29f223bce8043b84e8c8b282827790f"
- },
- {
- "decimalCount" : 18,
- "symbol" : "PAY",
- "name" : "TenX",
- "contractAddress" : "0xB97048628DB6B661D4C2aA833e95Dbe1A905B280"
- },
- {
- "decimalCount" : 6,
- "symbol" : "USDT",
- "name" : "Tether USD",
- "contractAddress" : "0xdac17f958d2ee523a2206206994597c13d831ec7"
- },
- {
- "decimalCount" : 18,
- "symbol" : "THETA",
- "name" : "Theta Token",
- "contractAddress" : "0x3883f5e181fccaf8410fa61e12b59bad963fb645"
- },
- {
- "decimalCount" : 6,
- "symbol" : "TRX",
- "name" : "TRON",
- "contractAddress" : "0xf230b790e05390fc8295f4d3f60332c93bed42e2"
- },
- {
- "decimalCount" : 18,
- "symbol" : "TUSD",
- "name" : "TrueUSD",
- "contractAddress" : "0x0000000000085d4780B73119b644AE5ecd22b376"
- },
- {
- "decimalCount" : 8,
- "symbol" : "UCASH",
- "name" : "UCASH",
- "contractAddress" : "0x92e52a1a235d9a103d970901066ce910aacefd37"
- },
- {
- "decimalCount" : 18,
- "symbol" : "UNI",
- "name" : "Uniswap",
- "contractAddress" : "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"
- },
- {
- "decimalCount" : 6,
- "symbol" : "USDC",
- "name" : "USD Coin",
- "contractAddress" : "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
- },
- {
- "decimalCount" : 18,
- "symbol" : "VIB",
- "name" : "Viberate",
- "contractAddress" : "0x2C974B2d0BA1716E644c1FC59982a89DDD2fF724"
- },
- {
- "decimalCount" : 8,
- "symbol" : "VGX",
- "name" : "Voyager",
- "contractAddress" : "0x5af2be193a6abca9c8817001f45744777db30756"
- },
- {
- "decimalCount" : 18,
- "symbol" : "WTC",
- "name" : "Waltonchain",
- "contractAddress" : "0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74"
- },
- {
- "decimalCount" : 8,
- "symbol" : "WAX",
- "name" : "WAX",
- "contractAddress" : "0x39Bb259F66E1C59d5ABEF88375979b4D20D98022"
- },
- {
- "decimalCount" : 18,
- "symbol" : "WETH",
- "name" : "Wrapped Ether",
- "contractAddress" : "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
- },
- {
- "decimalCount" : 18,
- "symbol" : "WPR",
- "name" : "WePower",
- "contractAddress" : "0x4CF488387F035FF08c371515562CBa712f9015d4"
- },
- {
- "decimalCount" : 18,
- "symbol" : "WINGS",
- "name" : "Wings",
- "contractAddress" : "0x667088b212ce3d06a1b553a7221E1fD19000d9aF"
- },
- {
- "decimalCount" : 6,
- "symbol" : "WRC",
- "name" : "Worldcore",
- "contractAddress" : "0x72adadb447784dd7ab1f472467750fc485e4cb2d"
- },
- {
- "decimalCount" : 18,
- "symbol" : "ZLA",
- "name" : "Zilla",
- "contractAddress" : "0xfd8971d5e8e1740ce2d0a84095fca4de729d0c16"
- },
- {
- "decimalCount" : 0,
- "symbol" : "GD",
- "name" : "Zilla GD",
- "contractAddress" : "0xb4cebdb66b3a5183fd4764a25cad1fc109535016"
- },
- {
- "decimalCount" : 12,
- "symbol" : "ZIL",
- "name" : "Zilliqa",
- "contractAddress" : "0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27"
- }
-]
diff --git a/app/src/main/assets/ethereum_tokens.json b/app/src/main/assets/ethereum_tokens.json
new file mode 100644
index 0000000000..d8a8c0cd97
--- /dev/null
+++ b/app/src/main/assets/ethereum_tokens.json
@@ -0,0 +1,4765 @@
+[
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7FF4169a6B5122b664c51c95727d87750eC07c84/logo.png",
+ "symbol" : "10SET",
+ "name" : "10Set Token",
+ "contractAddress" : "0x7FF4169a6B5122b664c51c95727d87750eC07c84",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x111111111117dC0aa78b770fA6A738034120C302/logo.png",
+ "symbol" : "1INCH",
+ "name" : "1INCH Token",
+ "contractAddress" : "0x111111111117dC0aa78b770fA6A738034120C302",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBBc2AE13b23d715c30720F079fcd9B4a74093505/logo.png",
+ "symbol" : "ERN",
+ "name" : "@EthernityChain $ERN Token",
+ "contractAddress" : "0xBBc2AE13b23d715c30720F079fcd9B4a74093505",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "1ST",
+ "name" : "FirstBlood",
+ "contractAddress" : "0xAf30D2a7E90d7DC361c8C4585e9BB7D2F6f15bc7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/logo.png",
+ "symbol" : "AAVE",
+ "name" : "Aave Token",
+ "contractAddress" : "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "ABT",
+ "name" : "ArcBlock",
+ "contractAddress" : "0xb98d4c97425d9908e66e53a6fdf673acca0be986",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xADE00C28244d5CE17D72E40330B1c318cD12B7c3/logo.png",
+ "symbol" : "ADX",
+ "name" : "AdEx Network",
+ "contractAddress" : "0xADE00C28244d5CE17D72E40330B1c318cD12B7c3",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "AE",
+ "name" : "Aeternity",
+ "contractAddress" : "0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "AION",
+ "name" : "Aion",
+ "contractAddress" : "0x4CEdA7906a5Ed2179785Cd3A40A69ee8bc99C466",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8eB24319393716668D768dCEC29356ae9CfFe285/logo.png",
+ "symbol" : "AGI",
+ "name" : "SingularityNET Token",
+ "contractAddress" : "0x8eB24319393716668D768dCEC29356ae9CfFe285",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x626E8036dEB333b408Be468F951bdB42433cBF18/logo.png",
+ "symbol" : "AIOZ",
+ "name" : "AIOZ Network",
+ "contractAddress" : "0x626E8036dEB333b408Be468F951bdB42433cBF18",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x00a8b738E453fFd858a7edf03bcCfe20412f0Eb0/logo.png",
+ "symbol" : "ALBT",
+ "name" : "AllianceBlock Token",
+ "contractAddress" : "0x00a8b738E453fFd858a7edf03bcCfe20412f0Eb0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "AMLT",
+ "name" : "AMLT by Coinfirm",
+ "contractAddress" : "0xca0e7269600d353f70b14ad118a49575455c0f2f",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x95a4492F028aa1fd432Ea71146b433E7B4446611/logo.png",
+ "symbol" : "APY",
+ "name" : "APY Governance Token",
+ "contractAddress" : "0x95a4492F028aa1fd432Ea71146b433E7B4446611",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "ARN",
+ "name" : "Aeron",
+ "contractAddress" : "0xBA5F11b16B155792Cf3B2E6880E8706859A8AEB6",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 4,
+ "symbol" : "AST",
+ "name" : "Airswap",
+ "contractAddress" : "0x27054b13b1b798b345b591a4d22e6562d47ea75a",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "AVM",
+ "name" : "AVM Ecosystem",
+ "contractAddress" : "0x74004a7227615fb52b82d17ffabfa376907d8a4d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "ELF",
+ "name" : "Aelf",
+ "contractAddress" : "0xbf2179859fc6d5bee9bf9158632dc51678a4100e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "AMB",
+ "name" : "Amber",
+ "contractAddress" : "0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfF20817765cB7f73d4bde2e66e067E58D11095C2/logo.png",
+ "symbol" : "AMP",
+ "name" : "Amp",
+ "contractAddress" : "0xfF20817765cB7f73d4bde2e66e067E58D11095C2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png",
+ "symbol" : "AMPL",
+ "name" : "Ampleforth",
+ "contractAddress" : "0xD46bA6D942050d489DBd938a2C909A5d5039A161",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa117000000f279D81A1D3cc75430fAA017FA5A2e/logo.png",
+ "symbol" : "ANT",
+ "name" : "Aragon Network Token",
+ "contractAddress" : "0xa117000000f279D81A1D3cc75430fAA017FA5A2e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "APPC",
+ "name" : "AppCoins",
+ "contractAddress" : "0x1a7a8bd9106f2b8d977e08582dc7d24c723ab0db",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1337DEF16F9B486fAEd0293eb623Dc8395dFE46a/logo.png",
+ "symbol" : "ARMOR",
+ "name" : "Armor",
+ "contractAddress" : "0x1337DEF16F9B486fAEd0293eb623Dc8395dFE46a",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 0,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdacD69347dE42baBfAEcD09dC88958378780FB62/logo.png",
+ "symbol" : "ATRI",
+ "name" : "AtariToken",
+ "contractAddress" : "0xdacD69347dE42baBfAEcD09dC88958378780FB62",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "ATMI",
+ "name" : "Atonomi",
+ "contractAddress" : "0x97aeb5066e1a590e868b511457beb6fe99d329f5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998/logo.png",
+ "symbol" : "AUDIO",
+ "name" : "Audius",
+ "contractAddress" : "0x18aAA7115705e8be94bfFEBDE57Af9BFc265B998",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "AUTO",
+ "name" : "CUBE",
+ "contractAddress" : "0x622dFfCc4e83C64ba959530A5a5580687a57581b",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF5D669627376EBd411E34b98F19C868c8ABA5ADA/logo.png",
+ "symbol" : "AXS",
+ "name" : "Axie Infinity",
+ "contractAddress" : "0xF5D669627376EBd411E34b98F19C868c8ABA5ADA",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1C9491865a1DE77C5b6e19d2E6a5F1D7a6F2b25F/logo.png",
+ "symbol" : "MATTER",
+ "name" : "Antimatter.Finance Governance Token",
+ "contractAddress" : "0x1C9491865a1DE77C5b6e19d2E6a5F1D7a6F2b25F",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x71F85B2E46976bD21302B64329868fd15eb0D127/logo.png",
+ "symbol" : "AXN",
+ "name" : "Axion",
+ "contractAddress" : "0x71F85B2E46976bD21302B64329868fd15eb0D127",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xba100000625a3754423978a60c9317c58a424e3D/logo.png",
+ "symbol" : "BAL",
+ "name" : "Balancer",
+ "contractAddress" : "0xba100000625a3754423978a60c9317c58a424e3D",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "BIX",
+ "name" : "BIX Token",
+ "contractAddress" : "0xb3104b4b9da82025e8b9f8fb28b3553ce2f67069",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5Dc02Ea99285E17656b8350722694c35154DB1E8/logo.png",
+ "symbol" : "BOND",
+ "name" : "BOND",
+ "contractAddress" : "0x5Dc02Ea99285E17656b8350722694c35154DB1E8",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "BRD",
+ "name" : "BRD Token",
+ "contractAddress" : "0x558ec3152e2eb2174905cd19aea4e34a23de9ad6",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "BNT",
+ "name" : "Bancor",
+ "contractAddress" : "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55/logo.png",
+ "symbol" : "BAND",
+ "name" : "BandToken",
+ "contractAddress" : "0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x54C9EA2E9C9E8eD865Db4A4ce6711C2a0d5063Ba/logo.png",
+ "symbol" : "BART",
+ "name" : "BarterTrade",
+ "contractAddress" : "0x54C9EA2E9C9E8eD865Db4A4ce6711C2a0d5063Ba",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x07150e919B4De5fD6a63DE1F9384828396f25fDC/logo.png",
+ "symbol" : "BASE",
+ "name" : "Base Protocol",
+ "contractAddress" : "0x07150e919B4De5fD6a63DE1F9384828396f25fDC",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0D8775F648430679A709E98d2b0Cb6250d2887EF/logo.png",
+ "symbol" : "BAT",
+ "name" : "Basic Attention Token",
+ "contractAddress" : "0x0D8775F648430679A709E98d2b0Cb6250d2887EF",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA91ac63D040dEB1b7A5E4d4134aD23eb0ba07e14/logo.png",
+ "symbol" : "BEL",
+ "name" : "Bella",
+ "contractAddress" : "0xA91ac63D040dEB1b7A5E4d4134aD23eb0ba07e14",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "BNB",
+ "name" : "Binance Coin",
+ "contractAddress" : "0xb8c77482e45f1f44de1745f52c74426c631bdd52",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "BUSD",
+ "name" : "Binance USD",
+ "contractAddress" : "0x4fabb145d64652a948d72533023f6e7a623c7c53",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x67c597624B17b16fb77959217360B7cD18284253/logo.png",
+ "symbol" : "MARK",
+ "name" : "Benchmark",
+ "contractAddress" : "0x67c597624B17b16fb77959217360B7cD18284253",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7671904eed7f10808B664fc30BB8693FD7237abF/logo.png",
+ "symbol" : "BBR",
+ "name" : "BitberryToken",
+ "contractAddress" : "0x7671904eed7f10808B664fc30BB8693FD7237abF",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCF3C8Be2e2C42331Da80EF210e9B1b307C03d36A/logo.png",
+ "symbol" : "BEPRO",
+ "name" : "BetProtocolToken",
+ "contractAddress" : "0xCF3C8Be2e2C42331Da80EF210e9B1b307C03d36A",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0c7D5ae016f806603CB1782bEa29AC69471CAb9c/logo.png",
+ "symbol" : "BFC",
+ "name" : "Bifrost",
+ "contractAddress" : "0x0c7D5ae016f806603CB1782bEa29AC69471CAb9c",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9BE89D2a4cd102D8Fecc6BF9dA793be995C22541/logo.png",
+ "symbol" : "BBTC",
+ "name" : "Binance Wrapped BTC",
+ "contractAddress" : "0x9BE89D2a4cd102D8Fecc6BF9dA793be995C22541",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x25e1474170c4c0aA64fa98123bdc8dB49D7802fa/logo.png",
+ "symbol" : "BID",
+ "name" : "Bidao",
+ "contractAddress" : "0x25e1474170c4c0aA64fa98123bdc8dB49D7802fa",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x70401dFD142A16dC7031c56E862Fc88Cb9537Ce0/logo.png",
+ "symbol" : "BIRD",
+ "name" : "Bird.Money",
+ "contractAddress" : "0x70401dFD142A16dC7031c56E862Fc88Cb9537Ce0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAec7e1f531Bb09115103C53ba76829910Ec48966/logo.png",
+ "symbol" : "BLANK",
+ "name" : "Blank Token",
+ "contractAddress" : "0xAec7e1f531Bb09115103C53ba76829910Ec48966",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "BLZ",
+ "name" : "Bluzelle",
+ "contractAddress" : "0x5732046a883704404f284ce41ffadd5b007fd668",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x725C263e32c72dDC3A19bEa12C5a0479a81eE688/logo.png",
+ "symbol" : "BMI",
+ "name" : "Bridge Mutual",
+ "contractAddress" : "0x725C263e32c72dDC3A19bEa12C5a0479a81eE688",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD2dDa223b2617cB616c1580db421e4cFAe6a8a85/logo.png",
+ "symbol" : "BONDLY",
+ "name" : "Bondly Token",
+ "contractAddress" : "0xD2dDa223b2617cB616c1580db421e4cFAe6a8a85",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "BTM",
+ "name" : "Bytom",
+ "contractAddress" : "0xcb97e65f07da24d46bcdd078ebebd7c6e6e3d750",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "CANDY",
+ "name" : "Candy",
+ "contractAddress" : "0xf2eab3a2034d3f6b63734d2e08262040e3ff7b48",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x954b890704693af242613edEf1B603825afcD708/logo.png",
+ "symbol" : "CARD",
+ "name" : "Cardstack",
+ "contractAddress" : "0x954b890704693af242613edEf1B603825afcD708",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "CAS",
+ "name" : "Cashaa",
+ "contractAddress" : "0xe8780b48bdb05f928697a5e8155f672ed91462f7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "CCC",
+ "name" : "Container Crypto Coin",
+ "contractAddress" : "0x9e3359f862b6c7f5c660cfd6d1aa6909b1d9504d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x17aC188e09A7890a1844E5E65471fE8b0CcFadF3/logo.png",
+ "symbol" : "CC10",
+ "name" : "Cryptocurrency Top 10 Tokens Index",
+ "contractAddress" : "0x17aC188e09A7890a1844E5E65471fE8b0CcFadF3",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26c8AFBBFE1EBaca03C2bB082E69D0476Bffe099/logo.png",
+ "symbol" : "CELL",
+ "name" : "Cellframe Token",
+ "contractAddress" : "0x26c8AFBBFE1EBaca03C2bB082E69D0476Bffe099",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 4,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d/logo.png",
+ "symbol" : "CEL",
+ "name" : "Celsius",
+ "contractAddress" : "0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "CFI",
+ "name" : "Cofound.it",
+ "contractAddress" : "0x12FEF5e57bF45873Cd9B62E9DBd7BFb99e32D73e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC4C2614E694cF534D407Ee49F8E44D125E4681c4/logo.png",
+ "symbol" : "CHAIN",
+ "name" : "Chain Games",
+ "contractAddress" : "0xC4C2614E694cF534D407Ee49F8E44D125E4681c4",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x63b4f3e3fa4e438698CE330e365E831F7cCD1eF4/logo.png",
+ "symbol" : "CFi",
+ "name" : "CyberFi Token",
+ "contractAddress" : "0x63b4f3e3fa4e438698CE330e365E831F7cCD1eF4",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "CNN",
+ "name" : "CNN Token",
+ "contractAddress" : "0x8713d26637cf49e1b6b4a7ce57106aabc9325343",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xDDB3422497E61e13543BeA06989C0789117555c5/logo.png",
+ "symbol" : "COTI",
+ "name" : "COTI Token",
+ "contractAddress" : "0xDDB3422497E61e13543BeA06989C0789117555c5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "CSP",
+ "name" : "Caspian",
+ "contractAddress" : "0xa6446d655a0c34bc4f05042ee88170d056cbaf45",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43/logo.png",
+ "symbol" : "CGG",
+ "name" : "ChainGuardians Governance Token",
+ "contractAddress" : "0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "CMT",
+ "name" : "CyberMiles",
+ "contractAddress" : "0xf85fEea2FdD81d51177F6b8F35F0e6734Ce45F5F",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "CND",
+ "name" : "Cindicator",
+ "contractAddress" : "0xd4c435f5b09f855c3317c8524cb1f586e42795fa",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "CVC",
+ "name" : "Civic",
+ "contractAddress" : "0x41e5560054824ea6b0732e656e3ad64e20e94e45",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69692D3345010a207b759a7D1af6fc7F38b35c5E/logo.png",
+ "symbol" : "CHADS",
+ "name" : "chads.vc",
+ "contractAddress" : "0x69692D3345010a207b759a7D1af6fc7F38b35c5E",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x514910771AF9Ca656af840dff83E8264EcF986CA/logo.png",
+ "symbol" : "Link",
+ "name" : "ChainLink",
+ "contractAddress" : "0x514910771AF9Ca656af840dff83E8264EcF986CA",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "TIME",
+ "name" : "Chronobank",
+ "contractAddress" : "0x6531f133e6DeeBe7F2dcE5A0441aA7ef330B4e53",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE61fDAF474Fac07063f2234Fb9e60C1163Cfa850/logo.png",
+ "symbol" : "COIN",
+ "name" : "Coin Utility Token",
+ "contractAddress" : "0xE61fDAF474Fac07063f2234Fb9e60C1163Cfa850",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc00e94Cb662C3520282E6f5717214004A7f26888/logo.png",
+ "symbol" : "COMP",
+ "name" : "Compound",
+ "contractAddress" : "0xc00e94Cb662C3520282E6f5717214004A7f26888",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x38e4adB44ef08F22F5B5b76A8f0c2d0dCbE7DcA1/logo.png",
+ "symbol" : "CVP",
+ "name" : "Concentrated Voting Power",
+ "contractAddress" : "0x38e4adB44ef08F22F5B5b76A8f0c2d0dCbE7DcA1",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc834Fa996fA3BeC7aAD3693af486ae53D8aA8B50/logo.png",
+ "symbol" : "CONV",
+ "name" : "Convergence",
+ "contractAddress" : "0xc834Fa996fA3BeC7aAD3693af486ae53D8aA8B50",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x62359Ed7505Efc61FF1D56fEF82158CcaffA23D7/logo.png",
+ "symbol" : "CORE",
+ "name" : "cVault.finance",
+ "contractAddress" : "0x62359Ed7505Efc61FF1D56fEF82158CcaffA23D7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2ba592F78dB6436527729929AAf6c908497cB200/logo.png",
+ "symbol" : "CREAM",
+ "name" : "Cream",
+ "contractAddress" : "0x2ba592F78dB6436527729929AAf6c908497cB200",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "symbol" : "CS",
+ "name" : "Credits",
+ "contractAddress" : "0x46b9ad944d1059450da1163511069c718f699d31",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaC0104Cca91D167873B8601d2e71EB3D4D8c33e0/logo.png",
+ "symbol" : "CWS",
+ "name" : "Crowns",
+ "contractAddress" : "0xaC0104Cca91D167873B8601d2e71EB3D4D8c33e0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "CRPT",
+ "name" : "Crypterium",
+ "contractAddress" : "0x08389495d7456e1951ddf7c3a1314a4bfb646d8b",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0f51bb10119727a7e5eA3538074fb341F56B09Ad/logo.png",
+ "symbol" : "DAO",
+ "name" : "DAO Maker Token",
+ "contractAddress" : "0x0f51bb10119727a7e5eA3538074fb341F56B09Ad",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26CE25148832C04f3d7F26F32478a9fe55197166/logo.png",
+ "symbol" : "DEXT",
+ "name" : "DEXTools",
+ "contractAddress" : "0x26CE25148832C04f3d7F26F32478a9fe55197166",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png",
+ "symbol" : "DAI",
+ "name" : "Dai Stablecoin",
+ "contractAddress" : "0x6B175474E89094C44Da98b954EedeAC495271d0F",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20/logo.png",
+ "symbol" : "DDIM",
+ "name" : "DuckDaoDime",
+ "contractAddress" : "0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE1c7E30C42C24582888C758984f6e382096786bd/logo.png",
+ "symbol" : "XCUR",
+ "name" : "Curate",
+ "contractAddress" : "0xE1c7E30C42C24582888C758984f6e382096786bd",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD533a949740bb3306d119CC777fa900bA034cd52/logo.png",
+ "symbol" : "CRV",
+ "name" : "Curve DAO Token",
+ "contractAddress" : "0xD533a949740bb3306d119CC777fa900bA034cd52",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x30f271C9E86D2B7d00a6376Cd96A1cFBD5F0b9b3/logo.png",
+ "symbol" : "DEC",
+ "name" : "Decentr",
+ "contractAddress" : "0x30f271C9E86D2B7d00a6376Cd96A1cFBD5F0b9b3",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9EA3b5b4EC044b70375236A281986106457b20EF/logo.png",
+ "symbol" : "DELTA",
+ "name" : "Delta Financial",
+ "contractAddress" : "0x9EA3b5b4EC044b70375236A281986106457b20EF",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "DENT",
+ "name" : "DENT",
+ "contractAddress" : "0x3597bfd533a99c9aa083587b074434e61eb0a258",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd379700999F4805Ce80aa32DB46A94dF64561108/logo.png",
+ "symbol" : "DETS",
+ "name" : "Dextrust",
+ "contractAddress" : "0xd379700999F4805Ce80aa32DB46A94dF64561108",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5cAf454Ba92e6F2c929DF14667Ee360eD9fD5b26/logo.png",
+ "symbol" : "DEV",
+ "name" : "Dev",
+ "contractAddress" : "0x5cAf454Ba92e6F2c929DF14667Ee360eD9fD5b26",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5F64Ab1544D28732F0A24F4713c2C8ec0dA089f0/logo.png",
+ "symbol" : "DEXTF",
+ "name" : "DEXTF Token",
+ "contractAddress" : "0x5F64Ab1544D28732F0A24F4713c2C8ec0dA089f0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc719d010B63E5bbF2C0551872CD5316ED26AcD83/logo.png",
+ "symbol" : "DIP",
+ "name" : "Decentralized Insurance Protocol",
+ "contractAddress" : "0xc719d010B63E5bbF2C0551872CD5316ED26AcD83",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x26E43759551333e57F073bb0772F50329A957b30/logo.png",
+ "symbol" : "DGVC",
+ "name" : "DegenVC",
+ "contractAddress" : "0x26E43759551333e57F073bb0772F50329A957b30",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x152687Bc4A7FCC89049cF119F9ac3e5aCF2eE7ef/logo.png",
+ "symbol" : "DHC",
+ "name" : "DeltaHub Community",
+ "contractAddress" : "0x152687Bc4A7FCC89049cF119F9ac3e5aCF2eE7ef",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0/logo.png",
+ "symbol" : "DF",
+ "name" : "dForce",
+ "contractAddress" : "0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "symbol" : "DGD",
+ "name" : "DigixDAO",
+ "contractAddress" : "0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x88EF27e69108B2633F8E1C184CC37940A075cC02/logo.png",
+ "symbol" : "DEGO",
+ "name" : "dego.finance",
+ "contractAddress" : "0x88EF27e69108B2633F8E1C184CC37940A075cC02",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x84cA8bc7997272c7CfB4D0Cd3D55cd942B3c9419/logo.png",
+ "symbol" : "DIA",
+ "name" : "DIA",
+ "contractAddress" : "0x84cA8bc7997272c7CfB4D0Cd3D55cd942B3c9419",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEd91879919B71bB6905f23af0A68d231EcF87b14/logo.png",
+ "symbol" : "DMG",
+ "name" : "DMM: Governance",
+ "contractAddress" : "0xEd91879919B71bB6905f23af0A68d231EcF87b14",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9cEB84f92A0561fa3Cc4132aB9c0b76A59787544/logo.png",
+ "symbol" : "DOKI",
+ "name" : "DokiDokiFinance",
+ "contractAddress" : "0x9cEB84f92A0561fa3Cc4132aB9c0b76A59787544",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "DOV",
+ "name" : "Dovu",
+ "contractAddress" : "0xac3211a5025414af2866ff09c23fc18bc97e79b1",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "DRGN",
+ "name" : "Dragon",
+ "contractAddress" : "0x419c4db4b9e25d6db2ad9691ccb832c8d9fda05e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "DNT",
+ "name" : "district0x",
+ "contractAddress" : "0x0abdace70d3790235af448c88547603b945604ea",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe/logo.png",
+ "symbol" : "DSLA",
+ "name" : "DSLA",
+ "contractAddress" : "0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC0bA369c8Db6eB3924965e5c4FD0b4C1B91e305F/logo.png",
+ "symbol" : "DUCK",
+ "name" : "DLP Duck Token",
+ "contractAddress" : "0xC0bA369c8Db6eB3924965e5c4FD0b4C1B91e305F",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17/logo.png",
+ "symbol" : "DYP",
+ "name" : "DeFiYieldProtocol",
+ "contractAddress" : "0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x740623d2c797b7D8D1EcB98e9b4Afcf99Ec31E14/logo.png",
+ "symbol" : "DYT",
+ "name" : "DoYourTip",
+ "contractAddress" : "0x740623d2c797b7D8D1EcB98e9b4Afcf99Ec31E14",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "EOS",
+ "name" : "EOS",
+ "contractAddress" : "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD/logo.png",
+ "symbol" : "ETH2x-FLI",
+ "name" : "ETH 2x Flexible Leverage Index",
+ "contractAddress" : "0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 2,
+ "symbol" : "EUR.AVM",
+ "name" : "EUR AVM",
+ "contractAddress" : "0xF01Cd2f1c9E42c509d309aC8C5b29B6dA8E64b1a",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 0,
+ "symbol" : "EDG",
+ "name" : "Edgeless",
+ "contractAddress" : "0x08711D3B02C8758F2FB3ab4e80228418a7F8e39c",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "EDO",
+ "name" : "Eidoo",
+ "contractAddress" : "0xced4e93198734ddaff8492d525bd258d49eb388e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x178c820f862B14f316509ec36b13123DA19A6054/logo.png",
+ "symbol" : "EWTB",
+ "name" : "Energy Web Token Bridged",
+ "contractAddress" : "0x178c820f862B14f316509ec36b13123DA19A6054",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c/logo.png",
+ "symbol" : "ENJ",
+ "name" : "Enjin Coin",
+ "contractAddress" : "0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa0246c9032bC3A600820415aE600c6388619A14D/logo.png",
+ "symbol" : "FARM",
+ "name" : "FARM Reward Token",
+ "contractAddress" : "0xa0246c9032bC3A600820415aE600c6388619A14D",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE5CAeF4Af8780E59Df925470b050Fb23C43CA68C/logo.png",
+ "symbol" : "FRM",
+ "name" : "Ferrum Network Token",
+ "contractAddress" : "0xE5CAeF4Af8780E59Df925470b050Fb23C43CA68C",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "FXC",
+ "name" : "Flexacoin",
+ "contractAddress" : "0x4a57E687b9126435a9B19E4A802113e266AdeBde",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8a40c222996f9F3431f63Bf80244C36822060f12/logo.png",
+ "symbol" : "FXF",
+ "name" : "Finxflo",
+ "contractAddress" : "0x8a40c222996f9F3431f63Bf80244C36822060f12",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x853d955aCEf822Db058eb8505911ED77F175b99e/logo.png",
+ "symbol" : "FRAX",
+ "name" : "Frax",
+ "contractAddress" : "0x853d955aCEf822Db058eb8505911ED77F175b99e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x29502fE4d233EF0b45C3647101Fa1252cE0634BD/logo.png",
+ "symbol" : "FROGE",
+ "name" : "Froge Finance",
+ "contractAddress" : "0x29502fE4d233EF0b45C3647101Fa1252cE0634BD",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b/logo.png",
+ "symbol" : "FUN",
+ "name" : "FunFair",
+ "contractAddress" : "0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8c15Ef5b4B21951d50E53E4fbdA8298FFAD25057/logo.png",
+ "symbol" : "FX",
+ "name" : "Function X",
+ "contractAddress" : "0x8c15Ef5b4B21951d50E53E4fbdA8298FFAD25057",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6BFf2fE249601ed0Db3a87424a2E923118BB0312/logo.png",
+ "symbol" : "FYZ",
+ "name" : "Fyooz",
+ "contractAddress" : "0x6BFf2fE249601ed0Db3a87424a2E923118BB0312",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8a854288a5976036A725879164Ca3e91d30c6A1B/logo.png",
+ "symbol" : "GET",
+ "name" : "GET",
+ "contractAddress" : "0x8a854288a5976036A725879164Ca3e91d30c6A1B",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "symbol" : "GNX",
+ "name" : "Genaro Network",
+ "contractAddress" : "0x6ec8a24cabdc339a06a172f8223ea557055adaa5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9F9c8ec3534c3cE16F928381372BfbFBFb9F4D24/logo.png",
+ "symbol" : "GLQ",
+ "name" : "GraphLinq",
+ "contractAddress" : "0x9F9c8ec3534c3cE16F928381372BfbFBFb9F4D24",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6810e776880C02933D47DB1b9fc05908e5386b96/logo.png",
+ "symbol" : "GNO",
+ "name" : "Gnosis Token",
+ "contractAddress" : "0x6810e776880C02933D47DB1b9fc05908e5386b96",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "GNT",
+ "name" : "Golem",
+ "contractAddress" : "0xa74476443119A942dE498590Fe1f2454d7D4aC0d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc3771d47E2Ab5A519E2917E61e23078d0C05Ed7f/logo.png",
+ "symbol" : "GTH",
+ "name" : "Gather",
+ "contractAddress" : "0xc3771d47E2Ab5A519E2917E61e23078d0C05Ed7f",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 2,
+ "symbol" : "GUSD",
+ "name" : "Gemini Dollar",
+ "contractAddress" : "0x056fd409e1d7a124bd7017459dfea2f387b6d5cd",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "GVT",
+ "name" : "Genesis Vision",
+ "contractAddress" : "0x103c3A209da59d3E7C4A89307e66521e081CFDF0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbEa98c05eEAe2f3bC8c3565Db7551Eb738c8CCAb/logo.png",
+ "symbol" : "GYSR",
+ "name" : "Geyser",
+ "contractAddress" : "0xbEa98c05eEAe2f3bC8c3565Db7551Eb738c8CCAb",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 0,
+ "symbol" : "GD",
+ "name" : "Zilla GD",
+ "contractAddress" : "0xb4cebdb66b3a5183fd4764a25cad1fc109535016",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x09e64c2B61a5f1690Ee6fbeD9baf5D6990F8dFd0/logo.png",
+ "symbol" : "GRO",
+ "name" : "Growth",
+ "contractAddress" : "0x09e64c2B61a5f1690Ee6fbeD9baf5D6990F8dFd0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc944E90C64B2c07662A292be6244BDf05Cda44a7/logo.png",
+ "symbol" : "GRT",
+ "name" : "Graph Token",
+ "contractAddress" : "0xc944E90C64B2c07662A292be6244BDf05Cda44a7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 3,
+ "symbol" : "GUP",
+ "name" : "Matchpool",
+ "contractAddress" : "0xf7B098298f7C69Fc14610bf71d5e02c60792894C",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png",
+ "symbol" : "HEX",
+ "name" : "HEX",
+ "contractAddress" : "0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "HPB",
+ "name" : "HPBCoin",
+ "contractAddress" : "0x38c6a68304cdefb9bec48bbfaaba5c5b47818bb2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x584bC13c7D411c00c01A62e8019472dE68768430/logo.png",
+ "symbol" : "HEGIC",
+ "name" : "Hegic",
+ "contractAddress" : "0x584bC13c7D411c00c01A62e8019472dE68768430",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEEF9f339514298C6A857EfCfC1A762aF84438dEE/logo.png",
+ "symbol" : "HEZ",
+ "name" : "Hermez Network Token",
+ "contractAddress" : "0xEEF9f339514298C6A857EfCfC1A762aF84438dEE",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6c6EE5e31d828De241282B9606C8e98Ea48526E2/logo.png",
+ "symbol" : "HOT",
+ "name" : "HoloToken",
+ "contractAddress" : "0x6c6EE5e31d828De241282B9606C8e98Ea48526E2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "HYDRO",
+ "name" : "Hydro",
+ "contractAddress" : "0xebbdf302c940c6bfd49c6b165f457fdb324649bc",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "ICX",
+ "name" : "ICON",
+ "contractAddress" : "0xb5a5f22694352c15b00323844ad545abb2b11028",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "ICN",
+ "name" : "ICONOMI",
+ "contractAddress" : "0x888666CA69E0f178DED6D75b5726Cee99A87D698",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEBd9D99A3982d547C5Bb4DB7E3b1F9F14b67Eb83/logo.png",
+ "symbol" : "ID",
+ "name" : "Everest ID",
+ "contractAddress" : "0xEBd9D99A3982d547C5Bb4DB7E3b1F9F14b67Eb83",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72630B1e3B42874bf335020Ba0249e3E9e47Bafc/logo.png",
+ "symbol" : "EPAN",
+ "name" : "Paypolitan Token",
+ "contractAddress" : "0x72630B1e3B42874bf335020Ba0249e3E9e47Bafc",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "GBX",
+ "name" : "Globitex",
+ "contractAddress" : "0x12fcd6463e66974cf7bbc24ffc4d40d6be458283",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf6537FE0df7F0Cc0985Cf00792CC98249E73EFa0/logo.png",
+ "symbol" : "GIV",
+ "name" : "GIVToken",
+ "contractAddress" : "0xf6537FE0df7F0Cc0985Cf00792CC98249E73EFa0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xeEAA40B28A2d1b0B08f6f97bB1DD4B75316c6107/logo.png",
+ "symbol" : "GOVI",
+ "name" : "GOVI",
+ "contractAddress" : "0xeEAA40B28A2d1b0B08f6f97bB1DD4B75316c6107",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 5,
+ "symbol" : "GTO",
+ "name" : "Gifto",
+ "contractAddress" : "0xc5bbae50781be1669306b9e001eff57a2957b09d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "IOST",
+ "name" : "IOStoken",
+ "contractAddress" : "0xfa1a856cfa3409cfa145fa4e20eb270df3eb21ab",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "ITC",
+ "name" : "IOT Chain",
+ "contractAddress" : "0x5e6b6d9abad9093fdc861ea1600eba1b355cd940",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30/logo.png",
+ "symbol" : "INJ",
+ "name" : "Injective Token",
+ "contractAddress" : "0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4B1E80cAC91e2216EEb63e29B957eB91Ae9C2Be8/logo.png",
+ "symbol" : "JUP",
+ "name" : "Jupiter",
+ "contractAddress" : "0x4B1E80cAC91e2216EEb63e29B957eB91Ae9C2Be8",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x85Eee30c52B0b379b046Fb0F85F4f3Dc3009aFEC/logo.png",
+ "symbol" : "KEEP",
+ "name" : "KEEP Token",
+ "contractAddress" : "0x85Eee30c52B0b379b046Fb0F85F4f3Dc3009aFEC",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x16980b3B4a3f9D89E33311B5aa8f80303E5ca4F8/logo.png",
+ "symbol" : "KEX",
+ "name" : "KIRA Network",
+ "contractAddress" : "0x16980b3B4a3f9D89E33311B5aa8f80303E5ca4F8",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "KIN",
+ "name" : "Kin",
+ "contractAddress" : "0x818Fc6C2Ec5986bc6E2CBf00939d90556aB12ce5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCbfef8fdd706cde6F208460f2Bf39Aa9c785F05D/logo.png",
+ "symbol" : "KINE",
+ "name" : "Kine Governance Token",
+ "contractAddress" : "0xCbfef8fdd706cde6F208460f2Bf39Aa9c785F05D",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44/logo.png",
+ "symbol" : "KP3R",
+ "name" : "Keep3rV1",
+ "contractAddress" : "0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1dD80016e3d4ae146Ee2EBB484e8edD92dacC4ce/logo.png",
+ "symbol" : "LEAD",
+ "name" : "Lead Token",
+ "contractAddress" : "0x1dD80016e3d4ae146Ee2EBB484e8edD92dacC4ce",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0fF6ffcFDa92c53F615a4A75D982f399C989366b/logo.png",
+ "symbol" : "LAYER",
+ "name" : "Unilayer",
+ "contractAddress" : "0x0fF6ffcFDa92c53F615a4A75D982f399C989366b",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD/logo.png",
+ "symbol" : "LRC",
+ "name" : "LoopringCoin V2",
+ "contractAddress" : "0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3593D125a4f7849a1B059E64F4517A86Dd60c95d/logo.png",
+ "symbol" : "OM",
+ "name" : "MANTRA DAO",
+ "contractAddress" : "0x3593D125a4f7849a1B059E64F4517A86Dd60c95d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "MAN",
+ "name" : "MATRIX AI Network",
+ "contractAddress" : "0xe25bcec5d3801ce3a794079bf94adf1b8ccd802d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "MYST",
+ "name" : "Mysterium",
+ "contractAddress" : "0xa645264C5603E96c3b0B078cdab68733794B0A71",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "NULS",
+ "name" : "Nuls",
+ "contractAddress" : "0xb91318f35bdb262e9423bc7c7c2a3a93dd93c92c",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfAd45E47083e4607302aa43c65fB3106F1cd7607/logo.png",
+ "symbol" : "HOGE",
+ "name" : "hoge.finance",
+ "contractAddress" : "0xfAd45E47083e4607302aa43c65fB3106F1cd7607",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "IXT",
+ "name" : "iXledger",
+ "contractAddress" : "0xfca47962d45adfdfd1ab2d972315db4ce7ccf094",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB9d99C33eA2d86EC5eC6b8A4dD816EBBA64404AF/logo.png",
+ "symbol" : "K21",
+ "name" : "k21.kanon.art",
+ "contractAddress" : "0xB9d99C33eA2d86EC5eC6b8A4dD816EBBA64404AF",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0F5D2fB29fb7d3CFeE444a200298f468908cC942/logo.png",
+ "symbol" : "MANA",
+ "name" : "Decentraland MANA",
+ "contractAddress" : "0x0F5D2fB29fb7d3CFeE444a200298f468908cC942",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b/logo.png",
+ "symbol" : "DPI",
+ "name" : "DefiPulse Index",
+ "contractAddress" : "0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "ENG",
+ "name" : "Enigma",
+ "contractAddress" : "0xf0ee6b27b759c9893ce4f094b49ad28fd15a23e4",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 16,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD6c67B93a7b248dF608a653d82a100556144c5DA/logo.png",
+ "symbol" : "EXNT",
+ "name" : "ExNetwork Community Token",
+ "contractAddress" : "0xD6c67B93a7b248dF608a653d82a100556144c5DA",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfffffffFf15AbF397dA76f1dcc1A1604F45126DB/logo.png",
+ "symbol" : "FSW",
+ "name" : "FalconSwap Token",
+ "contractAddress" : "0xfffffffFf15AbF397dA76f1dcc1A1604F45126DB",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF4d861575ecC9493420A3f5a14F85B13f0b50EB3/logo.png",
+ "symbol" : "FCL",
+ "name" : "Fractal Protocol Token",
+ "contractAddress" : "0xF4d861575ecC9493420A3f5a14F85B13f0b50EB3",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x956F47F50A910163D8BF957Cf5846D573E7f87CA/logo.png",
+ "symbol" : "FEI",
+ "name" : "Fei USD",
+ "contractAddress" : "0x956F47F50A910163D8BF957Cf5846D573E7f87CA",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "FSN",
+ "name" : "Fusion",
+ "contractAddress" : "0xd0352a019e9ab9d757776f532377aaebd36fd541",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdd974D5C2e2928deA5F71b9825b8b646686BD200/logo.png",
+ "symbol" : "KNC",
+ "name" : "Kyber Network Crystal",
+ "contractAddress" : "0xdd974D5C2e2928deA5F71b9825b8b646686BD200",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x67B6D479c7bB412C54e03dCA8E1Bc6740ce6b99C/logo.png",
+ "symbol" : "KYL",
+ "name" : "Kylin Network",
+ "contractAddress" : "0x67B6D479c7bB412C54e03dCA8E1Bc6740ce6b99C",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaE697F994Fc5eBC000F8e22EbFfeE04612f98A0d/logo.png",
+ "symbol" : "LGCY",
+ "name" : "LGCY Network",
+ "contractAddress" : "0xaE697F994Fc5eBC000F8e22EbFfeE04612f98A0d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0a50C93c762fDD6E56D86215C24AaAD43aB629aa/logo.png",
+ "symbol" : "LGO",
+ "name" : "LGO Token",
+ "contractAddress" : "0x0a50C93c762fDD6E56D86215C24AaAD43aB629aa",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6149C26Cd2f7b5CCdb32029aF817123F6E37Df5B/logo.png",
+ "symbol" : "LPOOL",
+ "name" : "Launchpool token",
+ "contractAddress" : "0x6149C26Cd2f7b5CCdb32029aF817123F6E37Df5B",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x80fB784B7eD66730e8b1DBd9820aFD29931aab03/logo.png",
+ "symbol" : "LEND",
+ "name" : "Lend",
+ "contractAddress" : "0x80fB784B7eD66730e8b1DBd9820aFD29931aab03",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0000000000095413afC295d19EDeb1Ad7B71c952/logo.png",
+ "symbol" : "LON",
+ "name" : "Tokenlon",
+ "contractAddress" : "0x0000000000095413afC295d19EDeb1Ad7B71c952",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4de2573e27E648607B50e1Cfff921A33E4A34405/logo.png",
+ "symbol" : "LST",
+ "name" : "Lendroid Support Token",
+ "contractAddress" : "0x4de2573e27E648607B50e1Cfff921A33E4A34405",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "LOOM",
+ "name" : "Loom",
+ "contractAddress" : "0xa4e8c3ec456107ea67d3075bf9e3df3a75823db0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3DB6Ba6ab6F95efed1a6E794caD492fAAabF294D/logo.png",
+ "symbol" : "LTO",
+ "name" : "LTO Network Token",
+ "contractAddress" : "0x3DB6Ba6ab6F95efed1a6E794caD492fAAabF294D",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA8b919680258d369114910511cc87595aec0be6D/logo.png",
+ "symbol" : "LYXe",
+ "name" : "LUKSO Token",
+ "contractAddress" : "0xA8b919680258d369114910511cc87595aec0be6D",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa393473d64d2F9F026B60b6Df7859A689715d092/logo.png",
+ "symbol" : "LTX",
+ "name" : "Lattice Token",
+ "contractAddress" : "0xa393473d64d2F9F026B60b6Df7859A689715d092",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "LUN",
+ "name" : "Lunyr",
+ "contractAddress" : "0xfa05A73FfE78ef8f1a739473e462c54bae6567D9",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd2877702675e6cEb975b4A1dFf9fb7BAF4C91ea9/logo.png",
+ "symbol" : "LUNA",
+ "name" : "Wrapped LUNA Token",
+ "contractAddress" : "0xd2877702675e6cEb975b4A1dFf9fb7BAF4C91ea9",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5f98805A4E8be255a32880FDeC7F6728C6568bA0/logo.png",
+ "symbol" : "LUSD",
+ "name" : "LUSD Stablecoin",
+ "contractAddress" : "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB4d930279552397bbA2ee473229f89Ec245bc365/logo.png",
+ "symbol" : "MAHA",
+ "name" : "MahaDAO",
+ "contractAddress" : "0xB4d930279552397bbA2ee473229f89Ec245bc365",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xfC98e825A2264D890F9a1e68ed50E1526abCcacD/logo.png",
+ "symbol" : "MCO2",
+ "name" : "Moss Carbon Credit",
+ "contractAddress" : "0xfC98e825A2264D890F9a1e68ed50E1526abCcacD",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAd4f86a25bbc20FfB751f2FAC312A0B4d8F88c64/logo.png",
+ "symbol" : "ROOM",
+ "name" : "OptionRoom Token",
+ "contractAddress" : "0xAd4f86a25bbc20FfB751f2FAC312A0B4d8F88c64",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5a666c7d92E5fA7Edcb6390E4efD6d0CDd69cF37/logo.png",
+ "symbol" : "MARSH",
+ "name" : "UnmarshalToken",
+ "contractAddress" : "0x5a666c7d92E5fA7Edcb6390E4efD6d0CDd69cF37",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69af81e73A73B40adF4f3d4223Cd9b1ECE623074/logo.png",
+ "symbol" : "MASK",
+ "name" : "Mask Network",
+ "contractAddress" : "0x69af81e73A73B40adF4f3d4223Cd9b1ECE623074",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4e352cF164E64ADCBad318C3a1e222E9EBa4Ce42/logo.png",
+ "symbol" : "MCB",
+ "name" : "MCDEX Token",
+ "contractAddress" : "0x4e352cF164E64ADCBad318C3a1e222E9EBa4Ce42",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png",
+ "symbol" : "MATIC",
+ "name" : "Matic Token",
+ "contractAddress" : "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD5525D397898e5502075Ea5E830d8914f6F0affe/logo.png",
+ "symbol" : "MEME",
+ "name" : "MEME",
+ "contractAddress" : "0xD5525D397898e5502075Ea5E830d8914f6F0affe",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2/logo.png",
+ "symbol" : "MKR",
+ "name" : "MakerDAO",
+ "contractAddress" : "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2/logo.png",
+ "symbol" : "MTA",
+ "name" : "Meta",
+ "contractAddress" : "0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "MTL",
+ "name" : "Metal",
+ "contractAddress" : "0xF433089366899D83a9f26A773D59ec7eCF30355e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72e364F2ABdC788b7E918bc238B21f109Cd634D7/logo.png",
+ "symbol" : "MVI",
+ "name" : "Metaverse Index",
+ "contractAddress" : "0x72e364F2ABdC788b7E918bc238B21f109Cd634D7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB26631c6dda06aD89B93C71400D25692de89c068/logo.png",
+ "symbol" : "MINDS",
+ "name" : "Minds",
+ "contractAddress" : "0xB26631c6dda06aD89B93C71400D25692de89c068",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEA1ea0972fa092dd463f2968F9bB51Cc4c981D71/logo.png",
+ "symbol" : "MOD",
+ "name" : "Modefi",
+ "contractAddress" : "0xEA1ea0972fa092dd463f2968F9bB51Cc4c981D71",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "MCO",
+ "name" : "Monaco",
+ "contractAddress" : "0xB63B606Ac810a52cCa15e44bB630fd42D8d1d83d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "NAS",
+ "name" : "Nebulas",
+ "contractAddress" : "0x5d65D971895Edc438f465c17DB6992698a52318D",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a/logo.png",
+ "symbol" : "TKN",
+ "name" : "Monolith TKN",
+ "contractAddress" : "0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa8c8CfB141A3bB59FEA1E2ea6B79b5ECBCD7b6ca/logo.png",
+ "symbol" : "NOIA",
+ "name" : "NOIA Token",
+ "contractAddress" : "0xa8c8CfB141A3bB59FEA1E2ea6B79b5ECBCD7b6ca",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206/logo.png",
+ "symbol" : "NEXO",
+ "name" : "Nexo",
+ "contractAddress" : "0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1cBb83EbcD552D5EBf8131eF8c9CD9d9BAB342bC/logo.png",
+ "symbol" : "NFY",
+ "name" : "Non-Fungible Yearn",
+ "contractAddress" : "0x1cBb83EbcD552D5EBf8131eF8c9CD9d9BAB342bC",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "NMR",
+ "name" : "Numeraire",
+ "contractAddress" : "0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x967da4048cD07aB37855c090aAF366e4ce1b9F48/logo.png",
+ "symbol" : "OCEAN",
+ "name" : "Ocean Protocol",
+ "contractAddress" : "0x967da4048cD07aB37855c090aAF366e4ce1b9F48",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png",
+ "symbol" : "OMG",
+ "name" : "OMG Network",
+ "contractAddress" : "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4c11249814f11b9346808179Cf06e71ac328c1b5/logo.png",
+ "symbol" : "ORAI",
+ "name" : "Oraichain Token",
+ "contractAddress" : "0x4c11249814f11b9346808179Cf06e71ac328c1b5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26/logo.png",
+ "symbol" : "OGN",
+ "name" : "OriginToken",
+ "contractAddress" : "0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0258F474786DdFd37ABCE6df6BBb1Dd5dfC4434a/logo.png",
+ "symbol" : "ORN",
+ "name" : "Orion Protocol",
+ "contractAddress" : "0x0258F474786DdFd37ABCE6df6BBb1Dd5dfC4434a",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "OST",
+ "name" : "OST",
+ "contractAddress" : "0x2C4e8f2D746113d0696cE89B35F0d8bF88E0AEcA",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x21BfBDa47A0B4B5b1248c767Ee49F7caA9B23697/logo.png",
+ "symbol" : "OVR",
+ "name" : "OVR",
+ "contractAddress" : "0x21BfBDa47A0B4B5b1248c767Ee49F7caA9B23697",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4575f41308EC1483f3d399aa9a2826d74Da13Deb/logo.png",
+ "symbol" : "OXT",
+ "name" : "Orchid",
+ "contractAddress" : "0x4575f41308EC1483f3d399aa9a2826d74Da13Deb",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1614F18Fc94f47967A3Fbe5FfcD46d4e7Da3D787/logo.png",
+ "symbol" : "PAID",
+ "name" : "PAID Network",
+ "contractAddress" : "0x1614F18Fc94f47967A3Fbe5FfcD46d4e7Da3D787",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8c8687fC965593DFb2F0b4EAeFD55E9D8df348df/logo.png",
+ "symbol" : "PAID",
+ "name" : "PAID Network",
+ "contractAddress" : "0x8c8687fC965593DFb2F0b4EAeFD55E9D8df348df",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x45804880De22913dAFE09f4980848ECE6EcbAf78/logo.png",
+ "symbol" : "PAXG",
+ "name" : "Paxos Gold",
+ "contractAddress" : "0x45804880De22913dAFE09f4980848ECE6EcbAf78",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "PAX",
+ "name" : "Paxos Standard",
+ "contractAddress" : "0x8e870d67f660d95d5be530380d0ec0bd388289e1",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbC396689893D065F41bc2C6EcbeE5e0085233447/logo.png",
+ "symbol" : "PERP",
+ "name" : "Perpetual",
+ "contractAddress" : "0xbC396689893D065F41bc2C6EcbeE5e0085233447",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5/logo.png",
+ "symbol" : "PICKLE",
+ "name" : "PickleToken",
+ "contractAddress" : "0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d/logo.png",
+ "symbol" : "PNK",
+ "name" : "Pinakion",
+ "contractAddress" : "0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "POE",
+ "name" : "Po.et",
+ "contractAddress" : "0x0e0989b1f9b8a38983c2ba8053269ca62ec9b195",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "PAL",
+ "name" : "PolicyPal Network",
+ "contractAddress" : "0xfeDAE5642668f8636A11987Ff386bfd215F942EE",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8B39B70E39Aa811b69365398e0aACe9bee238AEb/logo.png",
+ "symbol" : "PKF",
+ "name" : "PolkaFoundry",
+ "contractAddress" : "0x8B39B70E39Aa811b69365398e0aACe9bee238AEb",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD478161C952357F05f0292B56012Cd8457F1cfbF/logo.png",
+ "symbol" : "POLK",
+ "name" : "Polkamarkets",
+ "contractAddress" : "0xD478161C952357F05f0292B56012Cd8457F1cfbF",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x83e6f1E41cdd28eAcEB20Cb649155049Fac3D5Aa/logo.png",
+ "symbol" : "POLS",
+ "name" : "PolkastarterToken",
+ "contractAddress" : "0x83e6f1E41cdd28eAcEB20Cb649155049Fac3D5Aa",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "PRL",
+ "name" : "Oyster Pearl",
+ "contractAddress" : "0x1844b21593262668b7248d0f57a220caaba46ab9",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x362bc847A3a9637d3af6624EeC853618a43ed7D2/logo.png",
+ "symbol" : "PRQ",
+ "name" : "Parsiq Token",
+ "contractAddress" : "0x362bc847A3a9637d3af6624EeC853618a43ed7D2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "PTOY",
+ "name" : "Patientory",
+ "contractAddress" : "0x8Ae4BF2C33a8e667de34B54938B0ccD03Eb8CC06",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1796ae0b0fa4862485106a0de9b654eFE301D0b2/logo.png",
+ "symbol" : "PMON",
+ "name" : "Polkamon",
+ "contractAddress" : "0x1796ae0b0fa4862485106a0de9b654eFE301D0b2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "POLY",
+ "name" : "Polymath",
+ "contractAddress" : "0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "PPT",
+ "name" : "Populous",
+ "contractAddress" : "0xd4fa1460F537bb9085d22C7bcCB5DD450Ef28e3a",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "symbol" : "POWR",
+ "name" : "Power Ledger",
+ "contractAddress" : "0x595832f8fc6bf59c85c527fec3740a1b7a361269",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC57d533c50bC22247d49a368880fb49a1caA39F7/logo.png",
+ "symbol" : "PTF",
+ "name" : "PowerTrade Fuel Token",
+ "contractAddress" : "0xC57d533c50bC22247d49a368880fb49a1caA39F7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0CDF9acd87E940837ff21BB40c9fd55F68bba059/logo.png",
+ "symbol" : "MINT",
+ "name" : "Public Mint",
+ "contractAddress" : "0x0CDF9acd87E940837ff21BB40c9fd55F68bba059",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "symbol" : "QASH",
+ "name" : "QASH",
+ "contractAddress" : "0x618e75ac90b12c6049ba3b27f5d5f8651b0037f6",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4a220E6096B25EADb88358cb44068A3248254675/logo.png",
+ "symbol" : "QNT",
+ "name" : "Quant",
+ "contractAddress" : "0x4a220E6096B25EADb88358cb44068A3248254675",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6e0daDE58D2d89eBBe7aFc384e3E4f15b70b14D8/logo.png",
+ "symbol" : "QRX",
+ "name" : "QuiverX",
+ "contractAddress" : "0x6e0daDE58D2d89eBBe7aFc384e3E4f15b70b14D8",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x33D0568941C0C64ff7e0FB4fbA0B11BD37deEd9f/logo.png",
+ "symbol" : "RAMP",
+ "name" : "RAMP DEFI",
+ "contractAddress" : "0x33D0568941C0C64ff7e0FB4fbA0B11BD37deEd9f",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "symbol" : "RC1U03",
+ "name" : "RentalCoin 1.0",
+ "contractAddress" : "0x7384db1aaa06364d1e6e67f5434391d3eea2a038",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "RDN",
+ "name" : "Raiden Network",
+ "contractAddress" : "0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf1f955016EcbCd7321c7266BccFB96c68ea5E49b/logo.png",
+ "symbol" : "RLY",
+ "name" : "Rally",
+ "contractAddress" : "0xf1f955016EcbCd7321c7266BccFB96c68ea5E49b",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA4EED63db85311E22dF4473f87CcfC3DaDCFA3E3/logo.png",
+ "symbol" : "RBC",
+ "name" : "Rubic",
+ "contractAddress" : "0xA4EED63db85311E22dF4473f87CcfC3DaDCFA3E3",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8f8221aFbB33998d8584A2B05749bA73c37a938a/logo.png",
+ "symbol" : "REQ",
+ "name" : "Request Token",
+ "contractAddress" : "0x8f8221aFbB33998d8584A2B05749bA73c37a938a",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8762db106B2c2A0bccB3A80d1Ed41273552616E8/logo.png",
+ "symbol" : "RSR",
+ "name" : "Reserve Rights",
+ "contractAddress" : "0x8762db106B2c2A0bccB3A80d1Ed41273552616E8",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7dE91B204C1C737bcEe6F000AAA6569Cf7061cb7/logo.png",
+ "symbol" : "XRT",
+ "name" : "Robonomics",
+ "contractAddress" : "0x7dE91B204C1C737bcEe6F000AAA6569Cf7061cb7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCb5f72d37685C3D5aD0bB5F982443BC8FcdF570E/logo.png",
+ "symbol" : "ROOT",
+ "name" : "RootKit",
+ "contractAddress" : "0xCb5f72d37685C3D5aD0bB5F982443BC8FcdF570E",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3155BA85D5F96b2d030a4966AF206230e46849cb/logo.png",
+ "symbol" : "RUNE",
+ "name" : "THORChain ETH.RUNE",
+ "contractAddress" : "0x3155BA85D5F96b2d030a4966AF206230e46849cb",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "SAN",
+ "name" : "SAN",
+ "contractAddress" : "0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE/logo.png",
+ "symbol" : "SHIB",
+ "name" : "SHIBA INU",
+ "contractAddress" : "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x72e9D9038cE484EE986FEa183f8d8Df93f9aDA13/logo.png",
+ "symbol" : "SMARTCREDIT",
+ "name" : "SMARTCREDIT Token",
+ "contractAddress" : "0x72e9D9038cE484EE986FEa183f8d8Df93f9aDA13",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "SRN",
+ "name" : "SIRIN",
+ "contractAddress" : "0x68d57c9a1C35f63E2c83eE8e49A64e9d70528D25",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "SNM",
+ "name" : "SONM",
+ "contractAddress" : "0x983F6d60db79ea8cA4eB9968C6aFf8cfA04B3c63",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets//logo.png",
+ "symbol" : "SNX",
+ "name" : "Synthetix Network Token",
+ "contractAddress" : "0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x23B608675a2B2fB1890d3ABBd85c5775c51691d5/logo.png",
+ "symbol" : "SOCKS",
+ "name" : "Unisocks Edition 0",
+ "contractAddress" : "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0Ae055097C6d159879521C384F1D2123D1f195e6/logo.png",
+ "symbol" : "STAKE",
+ "name" : "xDai",
+ "contractAddress" : "0x0Ae055097C6d159879521C384F1D2123D1f195e6",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "SUB",
+ "name" : "Substratum",
+ "contractAddress" : "0x8D75959f1E61EC2571aa72798237101F084DE63a",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEa319e87Cf06203DAe107Dd8E5672175e3Ee976c/logo.png",
+ "symbol" : "SURF",
+ "name" : "SURF.Finance",
+ "contractAddress" : "0xEa319e87Cf06203DAe107Dd8E5672175e3Ee976c",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xCC4304A31d09258b0029eA7FE63d032f52e44EFe/logo.png",
+ "symbol" : "SWAP",
+ "name" : "TrustSwap Token",
+ "contractAddress" : "0xCC4304A31d09258b0029eA7FE63d032f52e44EFe",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB6ff96B8A8d214544Ca0dBc9B33f7AD6503eFD32/logo.png",
+ "symbol" : "SYNC",
+ "name" : "SYNC",
+ "contractAddress" : "0xB6ff96B8A8d214544Ca0dBc9B33f7AD6503eFD32",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB987D48Ed8f2C468D52D6405624EADBa5e76d723/logo.png",
+ "symbol" : "STBZ",
+ "name" : "Stabilize Token",
+ "contractAddress" : "0xB987D48Ed8f2C468D52D6405624EADBa5e76d723",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "SNT",
+ "name" : "Status",
+ "contractAddress" : "0x744d70FDBE2Ba4CF95131626614a1763DF805B9E",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "SEED",
+ "name" : "Superbloom Network",
+ "contractAddress" : "0x4e7bd88e3996f48e2a24d15e37ca4c02b4d134d2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1C9922314ED1415c95b9FD453c3818fd41867d0B/logo.png",
+ "symbol" : "TOWER",
+ "name" : "TOWER",
+ "contractAddress" : "0x1C9922314ED1415c95b9FD453c3818fd41867d0B",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd084B83C305daFD76AE3E1b4E1F1fe2eCcCb3988/logo.png",
+ "symbol" : "TVK",
+ "name" : "Terra Virtua Kolect",
+ "contractAddress" : "0xd084B83C305daFD76AE3E1b4E1F1fe2eCcCb3988",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaA7a9CA87d3694B5755f213B5D04094b8d0F0A6F/logo.png",
+ "symbol" : "TRAC",
+ "name" : "Trace Token",
+ "contractAddress" : "0xaA7a9CA87d3694B5755f213B5D04094b8d0F0A6F",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6F87D756DAf0503d08Eb8993686c7Fc01Dc44fB1/logo.png",
+ "symbol" : "TRADE",
+ "name" : "UniTrade",
+ "contractAddress" : "0x6F87D756DAf0503d08Eb8993686c7Fc01Dc44fB1",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B/logo.png",
+ "symbol" : "TRIBE",
+ "name" : "Tribe",
+ "contractAddress" : "0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0000000000085d4780B73119b644AE5ecd22b376/logo.png",
+ "symbol" : "TUSD",
+ "name" : "TrueUSD",
+ "contractAddress" : "0x0000000000085d4780B73119b644AE5ecd22b376",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4C19596f5aAfF459fA38B0f7eD92F11AE6543784/logo.png",
+ "symbol" : "TRU",
+ "name" : "TrustToken",
+ "contractAddress" : "0x4C19596f5aAfF459fA38B0f7eD92F11AE6543784",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e/logo.png",
+ "symbol" : "UBT",
+ "name" : "Unibright",
+ "contractAddress" : "0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "UCASH",
+ "name" : "UCASH",
+ "contractAddress" : "0x92e52a1a235d9a103d970901066ce910aacefd37",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828/logo.png",
+ "symbol" : "UMA",
+ "name" : "UMA Voting Token v1",
+ "contractAddress" : "0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2/logo.png",
+ "symbol" : "UMB",
+ "name" : "Umbrella",
+ "contractAddress" : "0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9Ed8e7C9604790F7Ec589F99b94361d8AAB64E5E/logo.png",
+ "symbol" : "UNISTAKE",
+ "name" : "Unistake",
+ "contractAddress" : "0x9Ed8e7C9604790F7Ec589F99b94361d8AAB64E5E",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x10Be9a8dAe441d276a5027936c3aADEd2d82bC15/logo.png",
+ "symbol" : "UMX",
+ "name" : "https://unimex.network/",
+ "contractAddress" : "0x10Be9a8dAe441d276a5027936c3aADEd2d82bC15",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x226f7b842E0F0120b7E194D05432b3fd14773a9D/logo.png",
+ "symbol" : "UNN",
+ "name" : "UNION Protocol Governance Token",
+ "contractAddress" : "0x226f7b842E0F0120b7E194D05432b3fd14773a9D",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "VIB",
+ "name" : "Viberate",
+ "contractAddress" : "0x2C974B2d0BA1716E644c1FC59982a89DDD2fF724",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3D3D35bb9bEC23b06Ca00fe472b50E7A4c692C30/logo.png",
+ "symbol" : "VIDYA",
+ "name" : "Vidya",
+ "contractAddress" : "0x3D3D35bb9bEC23b06Ca00fe472b50E7A4c692C30",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF938424F7210f31dF2Aee3011291b658f872e91e/logo.png",
+ "symbol" : "VISR",
+ "name" : "VISOR",
+ "contractAddress" : "0xF938424F7210f31dF2Aee3011291b658f872e91e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "WAX",
+ "name" : "WAX",
+ "contractAddress" : "0x39Bb259F66E1C59d5ABEF88375979b4D20D98022",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7a2Bc711E19ba6aff6cE8246C546E8c4B4944DFD/logo.png",
+ "symbol" : "WAXE",
+ "name" : "WAX Economic Token",
+ "contractAddress" : "0x7a2Bc711E19ba6aff6cE8246C546E8c4B4944DFD",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "WINGS",
+ "name" : "Wings",
+ "contractAddress" : "0x667088b212ce3d06a1b553a7221E1fD19000d9aF",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x66a0f676479Cee1d7373f3DC2e2952778BfF5bd6/logo.png",
+ "symbol" : "WISE",
+ "name" : "Wise Token",
+ "contractAddress" : "0x66a0f676479Cee1d7373f3DC2e2952778BfF5bd6",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBF494F02EE3FdE1F20BEE6242bCe2d1ED0c15e47/logo.png",
+ "symbol" : "WORLD",
+ "name" : "World Token",
+ "contractAddress" : "0xBF494F02EE3FdE1F20BEE6242bCe2d1ED0c15e47",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "symbol" : "WRC",
+ "name" : "Worldcore",
+ "contractAddress" : "0x72adadb447784dd7ab1f472467750fc485e4cb2d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png",
+ "symbol" : "WBTC",
+ "name" : "Wrapped BTC",
+ "contractAddress" : "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png",
+ "symbol" : "WETH",
+ "name" : "Wrapped Ethereum",
+ "contractAddress" : "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x09a3EcAFa817268f77BE1283176B946C4ff2E608/logo.png",
+ "symbol" : "MIR",
+ "name" : "Wrapped MIR Token",
+ "contractAddress" : "0x09a3EcAFa817268f77BE1283176B946C4ff2E608",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8888801aF4d980682e47f1A9036e589479e835C5/logo.png",
+ "symbol" : "MPH",
+ "name" : "88mph.app",
+ "contractAddress" : "0x8888801aF4d980682e47f1A9036e589479e835C5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9469D013805bFfB7D3DEBe5E7839237e535ec483/logo.png",
+ "symbol" : "RING",
+ "name" : "Evolution Land Global Token",
+ "contractAddress" : "0x9469D013805bFfB7D3DEBe5E7839237e535ec483",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 0,
+ "symbol" : "XJP",
+ "name" : "Digital JPY",
+ "contractAddress" : "0x39689fE671C01fcE173395f6BC45D4C332026666",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF063fE1aB7a291c5d06a86e14730b00BF24cB589/logo.png",
+ "symbol" : "SALE",
+ "name" : "DxSale.Network",
+ "contractAddress" : "0xF063fE1aB7a291c5d06a86e14730b00BF24cB589",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "XUC",
+ "name" : "Exchange Union",
+ "contractAddress" : "0xc324a2f6b05880503444451b8b27e6f9e63287cb",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xee573a945B01B788B9287CE062A0CFC15bE9fd86/logo.png",
+ "symbol" : "XED",
+ "name" : "Exeedme",
+ "contractAddress" : "0xee573a945B01B788B9287CE062A0CFC15bE9fd86",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2e1E15C44Ffe4Df6a0cb7371CD00d5028e571d14/logo.png",
+ "symbol" : "MTLX",
+ "name" : "Mettalex",
+ "contractAddress" : "0x2e1E15C44Ffe4Df6a0cb7371CD00d5028e571d14",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "NCASH",
+ "name" : "Nucleus Vision",
+ "contractAddress" : "0x809826cceab68c387726af962713b64cb5cb3cca",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA15C7Ebe1f07CaF6bFF097D8a589fb8AC49Ae5B3/logo.png",
+ "symbol" : "NPXS",
+ "name" : "Pundi X Token",
+ "contractAddress" : "0xA15C7Ebe1f07CaF6bFF097D8a589fb8AC49Ae5B3",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4fE83213D56308330EC302a8BD641f1d0113A4Cc/logo.png",
+ "symbol" : "NU",
+ "name" : "NuCypher",
+ "contractAddress" : "0x4fE83213D56308330EC302a8BD641f1d0113A4Cc",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "QRL",
+ "name" : "QRL",
+ "contractAddress" : "0x697beac28B09E122C4332D163985e8a73121b97F",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "PAY",
+ "name" : "TenX",
+ "contractAddress" : "0xB97048628DB6B661D4C2aA833e95Dbe1A905B280",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe3818504c1B32bF1557b16C238B2E01Fd3149C17/logo.png",
+ "symbol" : "PLR",
+ "name" : "PILLAR",
+ "contractAddress" : "0xe3818504c1B32bF1557b16C238B2E01Fd3149C17",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "PMTN",
+ "name" : "Peer Mountain",
+ "contractAddress" : "0xe91df2bb9bccd18c45f01843cab88768d64f2d32",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5BEfBB272290dD5b8521D4a938f6c4757742c430/logo.png",
+ "symbol" : "XFI",
+ "name" : "Xfinance",
+ "contractAddress" : "0x5BEfBB272290dD5b8521D4a938f6c4757742c430",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0f7F961648aE6Db43C75663aC7E5414Eb79b5704/logo.png",
+ "symbol" : "XIO",
+ "name" : "XIO Network",
+ "contractAddress" : "0x0f7F961648aE6Db43C75663aC7E5414Eb79b5704",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+
+ },
+ {
+ "decimalCount" : 9,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x607F4C5BB672230e8672085532f7e901544a7375/logo.png",
+ "symbol" : "RLC",
+ "name" : "iExec RLC",
+ "contractAddress" : "0x607F4C5BB672230e8672085532f7e901544a7375",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x89Ab32156e46F46D02ade3FEcbe5Fc4243B9AAeD/logo.png",
+ "symbol" : "PNT",
+ "name" : "pNetwork Token",
+ "contractAddress" : "0x89Ab32156e46F46D02ade3FEcbe5Fc4243B9AAeD",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x69A95185ee2a045CDC4bCd1b1Df10710395e4e23/logo.png",
+ "symbol" : "POOLZ",
+ "name" : "$Poolz Finance",
+ "contractAddress" : "0x69A95185ee2a045CDC4bCd1b1Df10710395e4e23",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ },
+ "symbol" : "QCX",
+ "name" : "QuickX Protocol",
+ "contractAddress" : "0xf9e5af7b42d31d51677c75bbbd37c1986ec79aee",
+ "customIcon" : "qcx"
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "QSP",
+ "name" : "Quantstamp",
+ "contractAddress" : "0x99ea4dB9EE77ACD40B119BD1dC4E33e1C070b80d",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919/logo.png",
+ "symbol" : "RAI",
+ "name" : "Rai Reflex Index",
+ "contractAddress" : "0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFca59Cd816aB1eaD66534D82bc21E7515cE441CF/logo.png",
+ "symbol" : "RARI",
+ "name" : "Rarible",
+ "contractAddress" : "0xFca59Cd816aB1eaD66534D82bc21E7515cE441CF",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x50DE6856358Cc35f3A9a57eAAA34BD4cB707d2cd/logo.png",
+ "symbol" : "RAZOR",
+ "name" : "RAZOR",
+ "contractAddress" : "0x50DE6856358Cc35f3A9a57eAAA34BD4cB707d2cd",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "RCN",
+ "name" : "Ripio Credit",
+ "contractAddress" : "0xf970b8e36e23f7fc3fd752eea86f8be8d83375a6",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xFE3E6a25e6b192A42a44ecDDCd13796471735ACf/logo.png",
+ "symbol" : "REEF",
+ "name" : "Reef.finance",
+ "contractAddress" : "0xFE3E6a25e6b192A42a44ecDDCd13796471735ACf",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x408e41876cCCDC0F92210600ef50372656052a38/logo.png",
+ "symbol" : "REN",
+ "name" : "Republic Token",
+ "contractAddress" : "0x408e41876cCCDC0F92210600ef50372656052a38",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1985365e9f78359a9B6AD760e32412f4a445E862/logo.png",
+ "symbol" : "REP",
+ "name" : "Reputation",
+ "contractAddress" : "0x1985365e9f78359a9B6AD760e32412f4a445E862",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x557B933a7C2c45672B610F8954A3deB39a51A8Ca/logo.png",
+ "symbol" : "REVV",
+ "name" : "REVV",
+ "contractAddress" : "0x557B933a7C2c45672B610F8954A3deB39a51A8Ca",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa1d6Df714F91DeBF4e0802A542E13067f31b8262/logo.png",
+ "symbol" : "RFOX",
+ "name" : "RFOX",
+ "contractAddress" : "0xa1d6Df714F91DeBF4e0802A542E13067f31b8262",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "RUFF",
+ "name" : "RUFF",
+ "contractAddress" : "0xf278c1ca969095ffddded020290cf8b5c424ace2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf21661D0D1d76d3ECb8e1B9F1c923DBfffAe4097/logo.png",
+ "symbol" : "RIO",
+ "name" : "Realio Network",
+ "contractAddress" : "0xf21661D0D1d76d3ECb8e1B9F1c923DBfffAe4097",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "RCC",
+ "name" : "Reality Clash",
+ "contractAddress" : "0x9b6443b0fb9c241a7fdac375595cea13e6b7807a",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x221657776846890989a759BA2973e427DfF5C9bB/logo.png",
+ "symbol" : "REPv2",
+ "name" : "Reputation",
+ "contractAddress" : "0x221657776846890989a759BA2973e427DfF5C9bB",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 0,
+ "symbol" : "REV",
+ "name" : "Revain",
+ "contractAddress" : "0x48f775efbe4f5ece6e0df2f7b5932df56823b990",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaf9f549774ecEDbD0966C52f250aCc548D3F36E5/logo.png",
+ "symbol" : "RFuel",
+ "name" : "Rio Fuel Token",
+ "contractAddress" : "0xaf9f549774ecEDbD0966C52f250aCc548D3F36E5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "RVT",
+ "name" : "Rivetz",
+ "contractAddress" : "0x3d1ba9be9f66b8ee101911bc36d3fb562eac2244",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB4EFd85c19999D84251304bDA99E90B92300Bd93/logo.png",
+ "symbol" : "RPL",
+ "name" : "Rocket Pool",
+ "contractAddress" : "0xB4EFd85c19999D84251304bDA99E90B92300Bd93",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3845badAde8e6dFF049820680d1F14bD3903a5d0/logo.png",
+ "symbol" : "SAND",
+ "name" : "SAND",
+ "contractAddress" : "0x3845badAde8e6dFF049820680d1F14bD3903a5d0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "SENT",
+ "name" : "SENTinel",
+ "contractAddress" : "0xa44e5137293e855b1b7bc7e2c6f8cd796ffcb037",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD23Ac27148aF6A2f339BD82D0e3CFF380b5093de/logo.png",
+ "symbol" : "SI",
+ "name" : "SIREN",
+ "contractAddress" : "0xD23Ac27148aF6A2f339BD82D0e3CFF380b5093de",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "STK",
+ "name" : "STK",
+ "contractAddress" : "0xae73b38d1c9a8b274127ec30160a4927c4d71824",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "SAI",
+ "name" : "Sai",
+ "contractAddress" : "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "SALT",
+ "name" : "SALT",
+ "contractAddress" : "0x4156D3342D5c385a87D264F90653733592000581",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9B02dD390a603Add5c07f9fd9175b7DABE8D63B7/logo.png",
+ "symbol" : "SPI",
+ "name" : "Shopping.io",
+ "contractAddress" : "0x9B02dD390a603Add5c07f9fd9175b7DABE8D63B7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 0,
+ "symbol" : "SNGLS",
+ "name" : "Singular DTV",
+ "contractAddress" : "0xaeC2E87E0A235266D9C5ADc9DEb4b2E29b54D009",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 0,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x37236CD05b34Cc79d3715AF2383E96dd7443dCF1/logo.png",
+ "symbol" : "SLP",
+ "name" : "Small Love Potion",
+ "contractAddress" : "0x37236CD05b34Cc79d3715AF2383E96dd7443dCF1",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x06A01a4d579479Dd5D884EBf61A31727A3d8D442/logo.png",
+ "symbol" : "Skey",
+ "name" : "SmartKey",
+ "contractAddress" : "0x06A01a4d579479Dd5D884EBf61A31727A3d8D442",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0488401c3F535193Fa8Df029d9fFe615A06E74E6/logo.png",
+ "symbol" : "SRK",
+ "name" : "SparkPoint",
+ "contractAddress" : "0x0488401c3F535193Fa8Df029d9fFe615A06E74E6",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xb753428af26E81097e7fD17f40c88aaA3E04902c/logo.png",
+ "symbol" : "SFI",
+ "name" : "Spice",
+ "contractAddress" : "0xb753428af26E81097e7fD17f40c88aaA3E04902c",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x00c83aeCC790e8a4453e5dD3B0B4b3680501a7A7/logo.png",
+ "symbol" : "SKL",
+ "name" : "SKALE",
+ "contractAddress" : "0x00c83aeCC790e8a4453e5dD3B0B4b3680501a7A7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x40FD72257597aA14C7231A7B1aaa29Fce868F677/logo.png",
+ "symbol" : "XOR",
+ "name" : "Sora",
+ "contractAddress" : "0x40FD72257597aA14C7231A7B1aaa29Fce868F677",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "STORJ",
+ "name" : "Storj",
+ "contractAddress" : "0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xbE9375C6a420D2eEB258962efB95551A5b722803/logo.png",
+ "symbol" : "STMX",
+ "name" : "StormX",
+ "contractAddress" : "0xbE9375C6a420D2eEB258962efB95551A5b722803",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "STQ",
+ "name" : "Storiqa",
+ "contractAddress" : "0x5c3a228510d246b78a3765c20221cbf3082b44a4",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "STORM",
+ "name" : "Storm",
+ "contractAddress" : "0xD0a4b8946Cb52f0661273bfbC6fD0E0C75Fc6433",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x990f341946A3fdB507aE7e52d17851B87168017c/logo.png",
+ "symbol" : "STRONG",
+ "name" : "Strong",
+ "contractAddress" : "0x990f341946A3fdB507aE7e52d17851B87168017c",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "STX",
+ "name" : "Stox",
+ "contractAddress" : "0x006BeA43Baa3f7A6f765F14f10A1a1b08334EF45",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "SNC",
+ "name" : "SunContract",
+ "contractAddress" : "0xF4134146AF2d511Dd5EA8cDB1C4AC88C57D60404",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xe53EC727dbDEB9E2d5456c3be40cFF031AB40A55/logo.png",
+ "symbol" : "SUPER",
+ "name" : "SuperFarm",
+ "contractAddress" : "0xe53EC727dbDEB9E2d5456c3be40cFF031AB40A55",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B3595068778DD592e39A122f4f5a5cF09C90fE2/logo.png",
+ "symbol" : "SUSHI",
+ "name" : "SushiSwap",
+ "contractAddress" : "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9/logo.png",
+ "symbol" : "SXP",
+ "name" : "Swipe",
+ "contractAddress" : "0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9f7229aF0c4b9740e207Ea283b9094983f78ba04/logo.png",
+ "symbol" : "TAD",
+ "name" : "Tadpole",
+ "contractAddress" : "0x9f7229aF0c4b9740e207Ea283b9094983f78ba04",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa1eD0364D53394209D61aE8bfdb8Ff50484D8c91/logo.png",
+ "symbol" : "TBC",
+ "name" : "TeraBlock Token",
+ "contractAddress" : "0xa1eD0364D53394209D61aE8bfdb8Ff50484D8c91",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "THETA",
+ "name" : "Theta Token",
+ "contractAddress" : "0x3883f5e181fccaf8410fa61e12b59bad963fb645",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x77777FeDdddFfC19Ff86DB637967013e6C6A116C/logo.png",
+ "symbol" : "TORN",
+ "name" : "TornadoCash",
+ "contractAddress" : "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "symbol" : "TRX",
+ "name" : "TRON",
+ "contractAddress" : "0xf230b790e05390fc8295f4d3f60332c93bed42e2",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0202Be363B8a4820f3F4DE7FaF5224fF05943AB1/logo.png",
+ "symbol" : "UFT",
+ "name" : "UniLend Finance Token",
+ "contractAddress" : "0x0202Be363B8a4820f3F4DE7FaF5224fF05943AB1",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
+ "symbol" : "USDC",
+ "name" : "USDC",
+ "contractAddress" : "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "STU",
+ "name" : "bitJob",
+ "contractAddress" : "0x0371a82e4a9d0a4312f3ee2ac9c6958512891372",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xBa21Ef4c9f433Ede00badEFcC2754B8E74bd538A/logo.png",
+ "symbol" : "SWFL",
+ "name" : "Swapfolio",
+ "contractAddress" : "0xBa21Ef4c9f433Ede00badEFcC2754B8E74bd538A",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa/logo.png",
+ "symbol" : "TBTC",
+ "name" : "tBTC",
+ "contractAddress" : "0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x57Ab1ec28D129707052df4dF418D58a2D46d5f51/logo.png",
+ "symbol" : "sUSD",
+ "name" : "Synth sUSD",
+ "contractAddress" : "0x57Ab1ec28D129707052df4dF418D58a2D46d5f51",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 2,
+ "symbol" : "TEL",
+ "name" : "Telcoin",
+ "contractAddress" : "0x467bccd9d29f223bce8043b84e8c8b282827790f",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xDcB01cc464238396E213a6fDd933E36796eAfF9f/logo.png",
+ "symbol" : "YLD",
+ "name" : "Yield",
+ "contractAddress" : "0xDcB01cc464238396E213a6fDd933E36796eAfF9f",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7F3EDcdD180Dbe4819Bd98FeE8929b5cEdB3AdEB/logo.png",
+ "symbol" : "XTK",
+ "name" : "xToken",
+ "contractAddress" : "0x7F3EDcdD180Dbe4819Bd98FeE8929b5cEdB3AdEB",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAE1eaAE3F627AAca434127644371b67B18444051/logo.png",
+ "symbol" : "YOP",
+ "name" : "YOP",
+ "contractAddress" : "0xAE1eaAE3F627AAca434127644371b67B18444051",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xf4CD3d3Fda8d7Fd6C5a500203e38640A70Bf9577/logo.png",
+ "symbol" : "Yf-DAI",
+ "name" : "YfDAI.finance",
+ "contractAddress" : "0xf4CD3d3Fda8d7Fd6C5a500203e38640A70Bf9577",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2e2f3246b6c65CCc4239c9Ee556EC143a7E5DE2c/logo.png",
+ "symbol" : "YFIM",
+ "name" : "yfi.mobi",
+ "contractAddress" : "0x2e2f3246b6c65CCc4239c9Ee556EC143a7E5DE2c",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xB1e9157c2Fdcc5a856C8DA8b2d89b6C32b3c1229/logo.png",
+ "symbol" : "ZEFU",
+ "name" : "Zenfuse Trading Platform Token",
+ "contractAddress" : "0xB1e9157c2Fdcc5a856C8DA8b2d89b6C32b3c1229",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 12,
+ "symbol" : "ZIL",
+ "name" : "Zilliqa",
+ "contractAddress" : "0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "ZLA",
+ "name" : "Zilla",
+ "contractAddress" : "0xfd8971d5e8e1740ce2d0a84095fca4de729d0c16",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE95A203B1a91a908F9B9CE46459d101078c2c3cb/logo.png",
+ "symbol" : "aEth",
+ "name" : "aEthereum",
+ "contractAddress" : "0xE95A203B1a91a908F9B9CE46459d101078c2c3cb",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x7b123f53421b1bF8533339BFBdc7C98aA94163db/logo.png",
+ "symbol" : "buidl",
+ "name" : "dfohub",
+ "contractAddress" : "0x7b123f53421b1bF8533339BFBdc7C98aA94163db",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5218E472cFCFE0b64A064F055B43b4cdC9EfD3A6/logo.png",
+ "symbol" : "eRSDL",
+ "name" : "UnFederalReserveToken",
+ "contractAddress" : "0x5218E472cFCFE0b64A064F055B43b4cdC9EfD3A6",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png",
+ "symbol" : "UNI",
+ "name" : "Uniswap",
+ "contractAddress" : "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 6,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdAC17F958D2ee523a2206206994597C13D831ec7/logo.png",
+ "symbol" : "USDT",
+ "name" : "Tether USD",
+ "contractAddress" : "0xdAC17F958D2ee523a2206206994597C13D831ec7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "VEE",
+ "name" : "BLOCKv",
+ "contractAddress" : "0x340d2bde5eb28c1eed91b2f790723e3b160613b7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF411903cbC70a74d22900a5DE66A2dda66507255/logo.png",
+ "symbol" : "VRA",
+ "name" : "VERA",
+ "contractAddress" : "0xF411903cbC70a74d22900a5DE66A2dda66507255",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1b40183EFB4Dd766f11bDa7A7c3AD8982e998421/logo.png",
+ "symbol" : "VSP",
+ "name" : "VesperToken",
+ "contractAddress" : "0x1b40183EFB4Dd766f11bDa7A7c3AD8982e998421",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "VGX",
+ "name" : "Voyager",
+ "contractAddress" : "0x5af2be193a6abca9c8817001f45744777db30756",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 4,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x9355372396e3F6daF13359B7b607a3374cc638e0/logo.png",
+ "symbol" : "WHALE",
+ "name" : "WHALE",
+ "contractAddress" : "0x9355372396e3F6daF13359B7b607a3374cc638e0",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "WTC",
+ "name" : "Waltonchain",
+ "contractAddress" : "0xb7cb1c96db6b22b0d3d9536e0108d062bd488f74",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4691937a7508860F876c9c0a2a617E7d9E945D4B/logo.png",
+ "symbol" : "WOO",
+ "name" : "Wootrade Network",
+ "contractAddress" : "0x4691937a7508860F876c9c0a2a617E7d9E945D4B",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "WPR",
+ "name" : "WePower",
+ "contractAddress" : "0x4CF488387F035FF08c371515562CBa712f9015d4",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x5228a22e72ccC52d415EcFd199F99D0665E7733b/logo.png",
+ "symbol" : "pBTC",
+ "name" : "pTokens BTC",
+ "contractAddress" : "0x5228a22e72ccC52d415EcFd199F99D0665E7733b",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D/logo.png",
+ "symbol" : "renBTC",
+ "name" : "renBTC",
+ "contractAddress" : "0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x3383c5a8969Dc413bfdDc9656Eb80A1408E4bA20/logo.png",
+ "symbol" : "wANATHA",
+ "name" : "Wrapped ANATHA",
+ "contractAddress" : "0x3383c5a8969Dc413bfdDc9656Eb80A1408E4bA20",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xa0afAA285Ce85974c3C881256cB7F225e3A1178a/logo.png",
+ "symbol" : "wCRES",
+ "name" : "Wrapped CRES",
+ "contractAddress" : "0xa0afAA285Ce85974c3C881256cB7F225e3A1178a",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e/logo.png",
+ "symbol" : "YFI",
+ "name" : "Yearn.Finance",
+ "contractAddress" : "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF94b5C5651c888d928439aB6514B93944eEE6F48/logo.png",
+ "symbol" : "YLD",
+ "name" : "Yield",
+ "contractAddress" : "0xF94b5C5651c888d928439aB6514B93944eEE6F48",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 10,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xb9EF770B6A5e12E45983C5D80545258aA38F3B78/logo.png",
+ "symbol" : "ZCN",
+ "name" : "0chain",
+ "contractAddress" : "0xb9EF770B6A5e12E45983C5D80545258aA38F3B78",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2eDf094dB69d6Dcd487f1B3dB9febE2eeC0dd4c5/logo.png",
+ "symbol" : "ZEE",
+ "name" : "ZeroSwapToken",
+ "contractAddress" : "0x2eDf094dB69d6Dcd487f1B3dB9febE2eeC0dd4c5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE41d2489571d322189246DaFA5ebDe1F4699F498/logo.png",
+ "symbol" : "ZRX",
+ "name" : "0x Protocol Token",
+ "contractAddress" : "0xE41d2489571d322189246DaFA5ebDe1F4699F498",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "customIconUrl" : "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x1337DEF18C680aF1f9f45cBcab6309562975b1dD/logo.png",
+ "symbol" : "arNXM",
+ "name" : "Armor NXM",
+ "contractAddress" : "0x1337DEF18C680aF1f9f45cBcab6309562975b1dD",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ },
+ {
+ "decimalCount" : 9,
+ "symbol" : "ADT",
+ "name" : "adToken",
+ "contractAddress" : "0xD0D6D6C5Fe4a677D343cC433536BB717bAe167dD",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : false
+ }
+ }
+]
diff --git a/app/src/main/assets/ethereum_tokens_testnet.json b/app/src/main/assets/ethereum_tokens_testnet.json
new file mode 100644
index 0000000000..8cd1d5a47e
--- /dev/null
+++ b/app/src/main/assets/ethereum_tokens_testnet.json
@@ -0,0 +1,46 @@
+[
+ {
+ "decimalCount" : 18,
+ "symbol" : "WEENUS",
+ "name" : "Weenus",
+ "contractAddress" : "0xaFF4481D10270F50f203E0763e2597776068CBc5",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ },
+ {
+ "decimalCount" : 18,
+ "symbol" : "XEENUS",
+ "name" : "Xeenus",
+ "contractAddress" : "0x022E292b44B5a146F2e8ee36Ff44D3dd863C915c",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ },
+ {
+ "decimalCount" : 8,
+ "symbol" : "YEENUS",
+ "name" : "Yeenus",
+ "contractAddress" : "0xc6fDe3FD2Cc2b173aEC24cc3f267cb3Cd78a26B7",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ },
+ {
+ "decimalCount" : 0,
+ "symbol" : "ZEENUS",
+ "name" : "Zeenus",
+ "contractAddress" : "0x1f9061B953bBa0E36BF50F21876132DcF276fC6e",
+ "blockchain" : {
+ "key" : "ethereum",
+ "curve" : "secp256k1",
+ "testnet" : true
+ }
+ }
+]
diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt
index 544e1d7cda..6f99403572 100644
--- a/app/src/main/java/com/tangem/tap/MainActivity.kt
+++ b/app/src/main/java/com/tangem/tap/MainActivity.kt
@@ -7,10 +7,7 @@ import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.snackbar.Snackbar
-import com.tangem.CardFilter
-import com.tangem.Config
import com.tangem.TangemSdk
-import com.tangem.commands.common.card.CardType
import com.tangem.tangem_sdk_new.extensions.init
import com.tangem.tap.common.DialogManager
import com.tangem.tap.common.IntentHandler
@@ -20,7 +17,6 @@ import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.TangemSdkManager
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
-import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.CoroutineExceptionHandler
@@ -30,7 +26,6 @@ import kotlinx.coroutines.Job
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.ref.WeakReference
-import java.util.*
import kotlin.coroutines.CoroutineContext
lateinit var tangemSdk: TangemSdk
@@ -64,24 +59,30 @@ class MainActivity : AppCompatActivity(), SnackbarHandler {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
+ systemActions()
store.state.globalState.feedbackManager?.updateAcivity(this)
- requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
store.dispatch(NavigationAction.ActivityCreated(WeakReference(this)))
- tangemSdk = TangemSdk.init(
- this, Config(cardFilter = CardFilter(EnumSet.allOf(CardType::class.java)))
- )
- tangemSdkManager = TangemSdkManager(this)
+ tangemSdk = TangemSdk.init(this, TangemSdkManager.config)
+ tangemSdkManager = TangemSdkManager(tangemSdk, this)
+
store.dispatch(WalletConnectAction.RestoreSessions)
}
+ private fun systemActions() {
+ // makes the status bar text dark
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ }
+
override fun onResume() {
super.onResume()
notificationsHandler = NotificationsHandler(fragment_container)
- if (supportFragmentManager.backStackEntryCount == 0 ||
- store.state.globalState.scanNoteResponse == null
- ) {
- store.dispatch(HomeAction.CheckIfFirstLaunch)
+
+ val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0
+ val isScannedBefore = store.state.globalState.scanResponse != null
+ val isOnboardingServiceActive = store.state.globalState.onboardingManager != null
+ if (backStackIsEmpty || (!isOnboardingServiceActive && !isScannedBefore)) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.Home))
}
intentHandler.handleIntent(intent)
diff --git a/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt b/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt
new file mode 100644
index 0000000000..a30af0f5d6
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt
@@ -0,0 +1,21 @@
+package com.tangem.tap.common
+
+import android.content.Context
+import android.net.Uri
+import androidx.browser.customtabs.CustomTabColorSchemeParams
+import androidx.browser.customtabs.CustomTabsIntent
+import com.tangem.tap.common.extensions.getColorCompat
+import com.tangem.wallet.R
+
+class CustomTabsManager {
+ fun openUrl(url: String, context: Context) {
+ val customTabsIntent = CustomTabsIntent.Builder()
+ .setDefaultColorSchemeParams(
+ CustomTabColorSchemeParams.Builder()
+ .setNavigationBarColor(context.getColorCompat(R.color.toolbarColor))
+ .build()
+ )
+ .build()
+ customTabsIntent.launchUrl(context, Uri.parse(url))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt
index 96992a5d7a..114c17c7cd 100644
--- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt
+++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt
@@ -2,9 +2,12 @@ package com.tangem.tap.common
import android.app.Dialog
import android.content.Context
+import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
+import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog
+import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import org.rekotlin.StoreSubscriber
@@ -37,28 +40,38 @@ class DialogManager : StoreSubscriber {
val context = context ?: return
if (dialog != null) return
- when (state.dialog) {
+ dialog = when (state.dialog) {
+ is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context)
+ is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
is WalletConnectDialog.UnsupportedCard ->
- dialog = SimpleAlertDialog.create(
- titleRes = R.string.wallet_connect,
- messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet,
- context = context
+ SimpleAlertDialog.create(
+ titleRes = R.string.wallet_connect,
+ messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet,
+ context = context
)
is WalletConnectDialog.OpeningSessionRejected -> {
- dialog = SimpleAlertDialog.create(
- titleRes = R.string.wallet_connect,
- messageRes = R.string.wallet_connect_same_wcuri,
- context = context
+ SimpleAlertDialog.create(
+ titleRes = R.string.wallet_connect,
+ messageRes = R.string.wallet_connect_same_wcuri,
+ context = context
+ )
+ }
+ is WalletConnectDialog.SessionTimeout -> {
+ SimpleAlertDialog.create(
+ titleRes = R.string.wallet_connect,
+ messageRes = R.string.wallet_connect_error_timeout,
+ context = context
)
}
is WalletConnectDialog.ApproveWcSession ->
- dialog = ApproveWcSessionDialog.create(state.dialog.session, context)
+ ApproveWcSessionDialog.create(state.dialog.session, context)
is WalletConnectDialog.ClipboardOrScanQr ->
- dialog = ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context)
+ ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context)
is WalletConnectDialog.RequestTransaction ->
- dialog = TransactionDialog.create(state.dialog.dialogData, context)
+ TransactionDialog.create(state.dialog.dialogData, context)
is WalletConnectDialog.PersonalSign ->
- dialog = PersonalSignDialog.create(state.dialog.data, context)
+ PersonalSignDialog.create(state.dialog.data, context)
+ else -> null
}
dialog?.show()
}
diff --git a/app/src/main/java/com/tangem/tap/common/Handler.kt b/app/src/main/java/com/tangem/tap/common/Handler.kt
new file mode 100644
index 0000000000..f36d4d1541
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/Handler.kt
@@ -0,0 +1,26 @@
+package com.tangem.tap.common
+
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+
+private val uiHandler = Handler(Looper.getMainLooper())
+private val backgroundHandler = Handler(HandlerThread("AppMainHandlerThread").apply { start() }.looper)
+
+fun postUi(ms: Long = 0, func: Runnable) {
+ if (ms == 0L) uiHandler.post { func.run() } else uiHandler.postDelayed(func, ms)
+}
+
+fun postBackground(ms: Long = 0, func: Runnable) {
+ if (ms == 0L) backgroundHandler.post { func.run() } else backgroundHandler.postDelayed(func, ms)
+}
+
+fun post(ms: Long = 0, func: Runnable) {
+ if (ms == 0L) {
+ func.run()
+ } else {
+ val currentLooper = Looper.myLooper() ?: return
+
+ Handler(currentLooper).postDelayed(func, ms)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt
index f30a481c98..a804d20624 100644
--- a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt
+++ b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt
@@ -5,16 +5,20 @@ import android.nfc.NfcAdapter
import android.nfc.Tag
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.domain.topup.TradeCryptoHelper
import com.tangem.tap.domain.walletconnect.WalletConnectManager
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.home.redux.HomeAction
+import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.store
+import timber.log.Timber
class IntentHandler {
fun handleIntent(intent: Intent?) {
handleBackgroundScan(intent)
handleWalletConnectLink(intent)
+ handleSellCurrencyCallback(intent)
}
private fun handleBackgroundScan(intent: Intent?) {
@@ -38,4 +42,26 @@ class IntentHandler {
}
}
+ private fun handleSellCurrencyCallback(intent: Intent?) {
+ val transactionID =
+ intent?.data?.getQueryParameter(TradeCryptoHelper.TRANSACTION_ID_PARAM) ?: return
+ val currency =
+ intent.data?.getQueryParameter(TradeCryptoHelper.CURRENCY_CODE_PARAM) ?: return
+ val amount =
+ intent.data?.getQueryParameter(TradeCryptoHelper.CURRENCY_AMOUNT_PARAM) ?: return
+ val destinationAddress =
+ intent.data?.getQueryParameter(TradeCryptoHelper.DEPOSIT_WALLET_ADDRESS_PARAM)
+ ?: return
+
+ Timber.d("MoonPay Sell: $amount $currency to $destinationAddress")
+
+ store.dispatch(WalletAction.TradeCryptoAction.SendCrypto(
+ currencyId = currency,
+ amount = amount,
+ destinationAddress = destinationAddress,
+ transactionId = transactionID
+ ))
+ }
+
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt
index 4395bd0922..8ddf2f5ad9 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt
@@ -8,4 +8,8 @@ enum class AnalyticsEvent(val event: String) {
APP_RATING_DISMISS("dismiss_rate_app_warning"),
APP_RATING_NEGATIVE("negative_rate_app_feedback"),
APP_RATING_POSITIVE("positive_rate_app_feedback"),
+ WC_SUCCESS_RESPONSE("wallet_connect_success_response"),
+ WC_INVALID_REQUEST("wallet_connect_invalid_request"),
+ WC_NEW_SESSION("wallet_connect_new_session"),
+ WC_SESSION_DISCONNECTED("wallet_connect_session_disconnected"),
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt
index 1dc3586808..1022f85c5a 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt
@@ -1,8 +1,15 @@
package com.tangem.tap.common.analytics
-import com.tangem.commands.common.card.Card
+import com.tangem.common.card.Card
+import com.tangem.common.core.TangemSdkError
interface AnalyticsHandler {
- fun triggerEvent(event: AnalyticsEvent, card: Card? = null)
+ fun triggerEvent(event: AnalyticsEvent, card: Card? = null, blockchain: String? = null)
+ fun logCardSdkError(
+ error: TangemSdkError,
+ actionToLog: FirebaseAnalyticsHandler.ActionToLog,
+ parameters: Map? = null,
+ card: Card? = null
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt
index d1d3ce7a38..33383e1089 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt
@@ -3,34 +3,141 @@ package com.tangem.tap.common.analytics
import android.os.Bundle
import androidx.core.os.bundleOf
import com.google.firebase.analytics.ktx.analytics
+import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.ktx.Firebase
-import com.tangem.commands.common.card.Card
+import com.tangem.common.card.Card
+import com.tangem.common.core.TangemSdkError
+import com.tangem.tap.common.extensions.filterNotNull
object FirebaseAnalyticsHandler : AnalyticsHandler {
- override fun triggerEvent(event: AnalyticsEvent, card: Card?) {
- Firebase.analytics.logEvent(event.event, setCardData(card))
+ override fun triggerEvent(event: AnalyticsEvent, card: Card?, blockchain: String?) {
+ Firebase.analytics.logEvent(event.event, setData(card, blockchain))
}
fun logException(name: String, throwable: Throwable) {
Firebase.analytics.logEvent(name, bundleOf(
- "message" to (throwable.message ?: "none"),
- "cause_message" to (throwable.cause?.message ?: "none"),
- "stack_trace" to throwable.stackTraceToString()
+ "message" to (throwable.message ?: "none"),
+ "cause_message" to (throwable.cause?.message ?: "none"),
+ "stack_trace" to throwable.stackTraceToString()
))
}
- private fun setCardData(card: Card?): Bundle {
+ override fun logCardSdkError(
+ error: TangemSdkError,
+ actionToLog: ActionToLog,
+ parameters: Map?,
+ card: Card?,
+ ) {
+ if (error is TangemSdkError.UserCancelled) return
+
+ val params = parameters?.toMutableMap() ?: mutableMapOf()
+ if (card != null) params + getParamsFromCard(card)
+ params[AnalyticsParam.ACTION] = actionToLog.key
+ params[AnalyticsParam.ERROR_CODE] = error.code.toString()
+ params[AnalyticsParam.ERROR_DESCRIPTION] = error.javaClass.simpleName
+ params[AnalyticsParam.ERROR_KEY] = "TangemSdkError"
+
+ params.forEach {
+ FirebaseCrashlytics.getInstance().setCustomKey(it.key.param, it.value)
+ }
+ val cardError = TangemSdk.map(error)
+ FirebaseCrashlytics.getInstance().recordException(cardError)
+ }
+
+ private fun getParamsFromCard(card: Card): Map {
+ return mapOf(
+ AnalyticsParam.FIRMWARE to card.firmwareVersion.stringValue,
+ AnalyticsParam.BATCH_ID to card.batchId
+ ).filterNotNull()
+ }
+
+ private fun setData(card: Card?, blockchain: String?): Bundle {
if (card == null) return bundleOf()
return bundleOf(
- AnalyticsParam.BLOCKCHAIN.param to card.cardData?.blockchainName,
- AnalyticsParam.BATCH_ID.param to card.cardData?.batchId,
- AnalyticsParam.FIRMWARE.param to card.firmwareVersion.version
+ AnalyticsParam.BLOCKCHAIN.param to (blockchain),
+ AnalyticsParam.BATCH_ID.param to card.batchId,
+ AnalyticsParam.FIRMWARE.param to card.firmwareVersion.stringValue
)
}
- private enum class AnalyticsParam(val param: String) {
+ fun logWcEvent(event: WcAnalyticsEvent) {
+ when (event) {
+ is WcAnalyticsEvent.Action -> {
+
+ Firebase.analytics.logEvent(
+ AnalyticsEvent.WC_SUCCESS_RESPONSE.event, bundleOf(
+ AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action.name
+ )
+ )
+ }
+ is WcAnalyticsEvent.Error -> {
+ mapOf(
+ AnalyticsParam.WALLET_CONNECT_ACTION to event.action?.name,
+ AnalyticsParam.ERROR_DESCRIPTION to event.error.message
+ )
+ .filterNotNull()
+ .forEach {
+ FirebaseCrashlytics.getInstance().setCustomKey(it.key.param, it.value)
+ }
+ FirebaseCrashlytics.getInstance().recordException(event.error)
+ }
+ is WcAnalyticsEvent.InvalidRequest ->
+ Firebase.analytics.logEvent(
+ AnalyticsEvent.WC_INVALID_REQUEST.event, bundleOf(
+ AnalyticsParam.WALLET_CONNECT_REQUEST.param to event.json
+ )
+ )
+ is WcAnalyticsEvent.Session -> {
+ val analyticsEvent = when (event.event) {
+ WcSessionEvent.Disconnect -> AnalyticsEvent.WC_SESSION_DISCONNECTED
+ WcSessionEvent.Connect -> AnalyticsEvent.WC_NEW_SESSION
+ }
+ Firebase.analytics.logEvent(
+ analyticsEvent.event, bundleOf(
+ AnalyticsParam.WALLET_CONNECT_DAPP_URL.param to event.url
+ )
+ )
+ }
+ }
+
+ }
+
+ enum class AnalyticsParam(val param: String) {
BLOCKCHAIN("blockchain"),
BATCH_ID("batch_id"),
FIRMWARE("firmware"),
+ ACTION("action"),
+ ERROR_DESCRIPTION("error_description"),
+ ERROR_CODE("error_code"),
+ NEW_SECURITY_OPTION("new_security_option"),
+ ERROR_KEY("Tangem SDK error key"),
+ WALLET_CONNECT_ACTION("wallet_connect_action"),
+ WALLET_CONNECT_REQUEST("wallet_connect_request"),
+ WALLET_CONNECT_DAPP_URL("wallet_connect_dapp_url"),
}
-}
\ No newline at end of file
+
+ enum class ActionToLog(val key: String) {
+ Scan("tap_scan_task"),
+ SendTransaction("send_transaction"),
+ WalletConnectSign("wallet_connect_personal_sign"),
+ WalletConnectTransaction("wallet_connect_tx_sign"),
+ ReadPinSettings("read_pin_settings"),
+ ChangeSecOptions("change_sec_options"),
+ CreateWallet("create_wallet"),
+ PurgeWallet("purge_wallet"),
+ WriteIssuerData("write_issuer_data"),
+ }
+
+ enum class WcSessionEvent { Disconnect, Connect }
+
+ sealed class WcAnalyticsEvent {
+ data class Error(val error: Throwable, val action: WcAction?) :
+ WcAnalyticsEvent()
+
+ data class Session(val event: WcSessionEvent, val url: String?) : WcAnalyticsEvent()
+ data class Action(val action: WcAction) : WcAnalyticsEvent()
+ data class InvalidRequest(val json: String?) : WcAnalyticsEvent()
+ }
+
+ enum class WcAction { PersonalSign, SignTransaction, SendTransaction }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt b/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt
new file mode 100644
index 0000000000..0d6b58db28
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/analytics/TangemSdkError.kt
@@ -0,0 +1,81 @@
+package com.tangem.tap.common.analytics
+
+import com.tangem.common.core.TangemSdkError
+
+object TangemSdk {
+
+ // This mapping is performed to group errors in FirebaseCrashlytics.
+ // At the moment, the errors in Crashlytics can only be grouped by their place of creation (class and line).
+ fun map(error: TangemSdkError): TangemSdkError {
+ return when (error) {
+ is TangemSdkError.TagLost -> TangemSdkError.TagLost()
+ is TangemSdkError.ExtendedLengthNotSupported -> TangemSdkError.ExtendedLengthNotSupported()
+ is TangemSdkError.SerializeCommandError -> TangemSdkError.SerializeCommandError()
+ is TangemSdkError.DeserializeApduFailed -> TangemSdkError.DeserializeApduFailed()
+ is TangemSdkError.EncodingFailedTypeMismatch -> TangemSdkError.EncodingFailedTypeMismatch(
+ error.customMessage)
+ is TangemSdkError.EncodingFailed -> TangemSdkError.EncodingFailed(error.customMessage)
+ is TangemSdkError.DecodingFailedMissingTag -> TangemSdkError.DecodingFailedMissingTag(
+ error.customMessage)
+ is TangemSdkError.DecodingFailedTypeMismatch -> TangemSdkError.DecodingFailedTypeMismatch(
+ error.customMessage)
+ is TangemSdkError.DecodingFailed -> TangemSdkError.DecodingFailed(error.customMessage)
+ is TangemSdkError.InvalidResponse -> TangemSdkError.InvalidResponse()
+ is TangemSdkError.UnknownStatus -> TangemSdkError.UnknownStatus(error.statusWord)
+ is TangemSdkError.ErrorProcessingCommand -> TangemSdkError.ErrorProcessingCommand()
+ is TangemSdkError.InvalidState -> TangemSdkError.InvalidState()
+ is TangemSdkError.InsNotSupported -> TangemSdkError.InsNotSupported()
+ is TangemSdkError.InvalidParams -> TangemSdkError.InvalidParams()
+ is TangemSdkError.NeedEncryption -> TangemSdkError.NeedEncryption()
+ is TangemSdkError.FileNotFound -> TangemSdkError.FileNotFound()
+ is TangemSdkError.WalletNotFound -> TangemSdkError.WalletNotFound()
+ is TangemSdkError.AlreadyPersonalized -> TangemSdkError.AlreadyPersonalized()
+ is TangemSdkError.CannotBeDepersonalized -> TangemSdkError.CannotBeDepersonalized()
+ is TangemSdkError.AccessCodeRequired -> TangemSdkError.AccessCodeRequired()
+ is TangemSdkError.CardReadWrongWallet -> TangemSdkError.CardReadWrongWallet()
+ is TangemSdkError.CardWithMaxZeroWallets -> TangemSdkError.CardWithMaxZeroWallets()
+ is TangemSdkError.AlreadyCreated -> TangemSdkError.AlreadyCreated()
+ is TangemSdkError.MaxNumberOfWalletsCreated -> TangemSdkError.MaxNumberOfWalletsCreated()
+ is TangemSdkError.PurgeWalletProhibited -> TangemSdkError.PurgeWalletProhibited()
+ is TangemSdkError.AccessCodeCannotBeChanged -> TangemSdkError.AccessCodeCannotBeChanged()
+ is TangemSdkError.PasscodeCannotBeChanged -> TangemSdkError.PasscodeCannotBeChanged()
+ is TangemSdkError.AccessCodeCannotBeDefault -> TangemSdkError.AccessCodeCannotBeDefault()
+ is TangemSdkError.NoRemainingSignatures -> TangemSdkError.NoRemainingSignatures()
+ is TangemSdkError.EmptyHashes -> TangemSdkError.EmptyHashes()
+ is TangemSdkError.HashSizeMustBeEqual -> TangemSdkError.HashSizeMustBeEqual()
+ is TangemSdkError.WalletIsNotCreated -> TangemSdkError.WalletIsNotCreated()
+ is TangemSdkError.SignHashesNotAvailable -> TangemSdkError.SignHashesNotAvailable()
+ is TangemSdkError.TooManyHashesInOneTransaction -> TangemSdkError.TooManyHashesInOneTransaction()
+ is TangemSdkError.ExtendedDataSizeTooLarge -> TangemSdkError.ExtendedDataSizeTooLarge()
+ is TangemSdkError.NotPersonalized -> TangemSdkError.NotPersonalized()
+ is TangemSdkError.NotActivated -> TangemSdkError.NotActivated()
+ is TangemSdkError.WalletIsPurged -> TangemSdkError.WalletIsPurged()
+ is TangemSdkError.PasscodeRequired -> TangemSdkError.PasscodeRequired()
+ is TangemSdkError.VerificationFailed -> TangemSdkError.VerificationFailed()
+ is TangemSdkError.DataSizeTooLarge -> TangemSdkError.DataSizeTooLarge()
+ is TangemSdkError.MissingCounter -> TangemSdkError.MissingCounter()
+ is TangemSdkError.OverwritingDataIsProhibited -> TangemSdkError.OverwritingDataIsProhibited()
+ is TangemSdkError.DataCannotBeWritten -> TangemSdkError.DataCannotBeWritten()
+ is TangemSdkError.MissingIssuerPubicKey -> TangemSdkError.MissingIssuerPubicKey()
+ is TangemSdkError.CardVerificationFailed -> TangemSdkError.CardVerificationFailed()
+ is TangemSdkError.WrongAccessCode -> TangemSdkError.WrongAccessCode()
+ is TangemSdkError.WrongPasscode -> TangemSdkError.WrongPasscode()
+ is TangemSdkError.UnknownError -> TangemSdkError.UnknownError()
+ is TangemSdkError.UserCancelled -> TangemSdkError.UserCancelled()
+ is TangemSdkError.Busy -> TangemSdkError.Busy()
+ is TangemSdkError.MissingPreflightRead -> TangemSdkError.MissingPreflightRead()
+ is TangemSdkError.WrongCardNumber -> TangemSdkError.WrongCardNumber()
+ is TangemSdkError.WrongCardType -> TangemSdkError.WrongCardType()
+ is TangemSdkError.CardError -> TangemSdkError.CardError()
+ is TangemSdkError.NotSupportedFirmwareVersion -> TangemSdkError.NotSupportedFirmwareVersion()
+ is TangemSdkError.WalletError -> TangemSdkError.WalletError()
+ is TangemSdkError.WalletCannotBeCreated -> TangemSdkError.WalletCannotBeCreated()
+ is TangemSdkError.UnsupportedCurve -> TangemSdkError.UnsupportedCurve()
+ is TangemSdkError.UnsupportedWalletConfig -> TangemSdkError.UnsupportedWalletConfig()
+ is TangemSdkError.CryptoUtilsError -> TangemSdkError.CryptoUtilsError()
+ is TangemSdkError.NetworkError -> TangemSdkError.NetworkError(error.customMessage)
+ is TangemSdkError.ExceptionError -> TangemSdkError.ExceptionError(error.throwable)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/entities/Button.kt b/app/src/main/java/com/tangem/tap/common/entities/Button.kt
index 986a170564..a2d4127c8f 100644
--- a/app/src/main/java/com/tangem/tap/common/entities/Button.kt
+++ b/app/src/main/java/com/tangem/tap/common/entities/Button.kt
@@ -1,3 +1,17 @@
package com.tangem.tap.common.entities
-open class Button(val enabled: Boolean)
\ No newline at end of file
+import com.tangem.tap.features.send.redux.states.ButtonState
+import com.tangem.tap.features.wallet.redux.ProgressState
+
+open class Button(val enabled: Boolean)
+
+open class IndeterminateProgressButton(
+ val state: ButtonState
+) : Button(state != ButtonState.DISABLED) {
+
+ val progressState: ProgressState
+ get() = when (state) {
+ ButtonState.PROGRESS -> ProgressState.Loading
+ else -> ProgressState.Done
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt
index d1c417e842..8492ae44f1 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt
@@ -14,4 +14,18 @@ fun MutableList.removeBy(predicate: (T) -> Boolean): Boolean {
val toRemove = this.filter(predicate)
this.removeAll(toRemove)
return toRemove.isNotEmpty()
+}
+
+fun MutableList.replaceBy(item: T, predicate: (T) -> Boolean): Boolean {
+ val toRemove = this.filter(predicate)
+ if (toRemove.isEmpty()) return false
+
+ val indexes = toRemove.map { indexOf(it) }
+ this.removeAll(toRemove)
+ indexes.forEach { this.add(it, item) }
+ return true
+}
+
+fun MutableList.replaceByOrAdd(item: T, predicate: (T) -> Boolean) {
+ if (!replaceBy(item, predicate)) add(item)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Coroutine.kt b/app/src/main/java/com/tangem/tap/common/extensions/Coroutine.kt
new file mode 100644
index 0000000000..63c62dfdc2
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Coroutine.kt
@@ -0,0 +1,16 @@
+package com.tangem.tap.common.extensions
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+/**
+[REDACTED_AUTHOR]
+ */
+suspend fun withMainContext(block: suspend CoroutineScope.() -> T) {
+ withContext(Dispatchers.Main, block)
+}
+
+suspend fun withIOContext(block: suspend CoroutineScope.() -> T) {
+ withContext(Dispatchers.IO, block)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Map.kt b/app/src/main/java/com/tangem/tap/common/extensions/Map.kt
new file mode 100644
index 0000000000..e970306219
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Map.kt
@@ -0,0 +1,4 @@
+package com.tangem.tap.common.extensions
+
+fun Map.filterNotNull(): Map =
+ filter { it.key != null && it.value != null } as Map
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt
index d56ae0af85..02c794329c 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt
@@ -1,9 +1,12 @@
package com.tangem.tap.common.extensions
+import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
+import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.redux.navigation.AppScreen
+import com.tangem.tap.common.redux.navigation.FragmentShareTransition
import com.tangem.tap.features.details.ui.DetailsConfirmFragment
import com.tangem.tap.features.details.ui.DetailsFragment
import com.tangem.tap.features.details.ui.DetailsSecurityFragment
@@ -13,17 +16,35 @@ import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectSessionsFragment
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
import com.tangem.tap.features.home.HomeFragment
+import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment
+import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment
+import com.tangem.tap.features.onboarding.products.wallet.OnboardingWalletFragment
import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.tokens.ui.AddTokensFragment
+import com.tangem.tap.features.wallet.ui.OnboardingTwinsFragment
import com.tangem.tap.features.wallet.ui.WalletDetailsFragment
import com.tangem.tap.features.wallet.ui.WalletFragment
-import com.tangem.tap.features.wallet.ui.dialogs.TwinsOnboardingFragment
import com.tangem.wallet.R
-fun FragmentActivity.openFragment(screen: AppScreen, addToBackStack: Boolean = true) {
+fun FragmentActivity.openFragment(
+ screen: AppScreen,
+ addToBackstack: Boolean,
+ fgShareTransition: FragmentShareTransition? = null
+) {
val transaction = this.supportFragmentManager.beginTransaction()
- transaction.replace(R.id.fragment_container, fragmentFactory(screen), screen.name)
- if (addToBackStack && screen != AppScreen.Home) transaction.addToBackStack(null)
+ val fragment = fragmentFactory(screen)
+ fgShareTransition?.apply {
+ fragment.sharedElementEnterTransition = enterTransitionSet
+ fragment.sharedElementReturnTransition = exitTransitionSet
+ transaction.setReorderingAllowed(true)
+ shareElements.forEach { shareElement ->
+ shareElement.wView.get()?.let { view ->
+ transaction.addSharedElement(view, shareElement.name)
+ }
+ }
+ }
+ transaction.replace(R.id.fragment_container, fragment, screen.name)
+ if (addToBackstack && screen != AppScreen.Home) transaction.addToBackStack(null)
transaction.commitAllowingStateLoss()
}
@@ -38,9 +59,22 @@ fun FragmentActivity.getPreviousScreen(): AppScreen? {
return tag?.let { AppScreen.valueOf(tag) }
}
+fun FragmentActivity.addOnBackPressedDispatcher(
+ isEnabled: Boolean = true,
+ onBackPressed: VoidCallback
+): OnBackPressedCallback = (object : OnBackPressedCallback(isEnabled) {
+ override fun handleOnBackPressed() {
+ onBackPressed()
+ }
+}).also { this.onBackPressedDispatcher.addCallback(it) }
+
private fun fragmentFactory(screen: AppScreen): Fragment {
return when (screen) {
AppScreen.Home -> HomeFragment()
+ AppScreen.OnboardingNote -> OnboardingNoteFragment()
+ AppScreen.OnboardingWallet -> OnboardingWalletFragment()
+ AppScreen.OnboardingTwins -> OnboardingTwinsFragment()
+ AppScreen.OnboardingOther -> OnboardingOtherCardsFragment()
AppScreen.Wallet -> WalletFragment()
AppScreen.Send -> SendFragment()
AppScreen.Details -> DetailsFragment()
@@ -49,7 +83,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
AppScreen.Disclaimer -> DisclaimerFragment()
AppScreen.CreateTwinWalletWarning -> TwinWalletWarningFragment()
AppScreen.CreateTwinWallet -> CreateTwinWalletFragment()
- AppScreen.TwinsOnboarding -> TwinsOnboardingFragment()
AppScreen.AddTokens -> AddTokensFragment()
AppScreen.WalletDetails -> WalletDetailsFragment()
AppScreen.WalletConnectSessions -> WalletConnectSessionsFragment()
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt b/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt
index e12711e3d0..d470ae2bf6 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt
@@ -1,7 +1,9 @@
package com.tangem.tap.common.extensions
+import android.graphics.PorterDuff
import android.widget.ImageView
import android.widget.TextView
+import androidx.core.content.ContextCompat
import com.squareup.picasso.Callback
import com.squareup.picasso.Picasso
import com.tangem.blockchain.common.Blockchain
@@ -48,6 +50,7 @@ fun Picasso.loadCurrenciesIcon(
imageView.colorFilter = null
textView.text = null
}
+ if (blockchain.isTestnet()) imageView.tint(R.color.tint)
}
})
}
@@ -70,12 +73,20 @@ private fun setOfflineCurrencyImage(
} else {
setBlockchainImage(imageView, textView, blockchain)
}
+ if (blockchain.isTestnet()) imageView.tint(R.color.tint)
+}
+
+fun ImageView.tint(colorRes: Int) {
+// val color = ContextCompat.getColor(context, colorRes);
+// ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(color));
+ this.setColorFilter(ContextCompat.getColor(context, colorRes), PorterDuff.Mode.DARKEN);
+
}
private fun setBlockchainImage(
imageView: ImageView,
textView: TextView,
- blockchain: Blockchain
+ blockchain: Blockchain,
) {
imageView.setImageResource(blockchain.getIconRes())
imageView.colorFilter = null
@@ -85,7 +96,7 @@ private fun setBlockchainImage(
private fun setTokenImage(
imageView: ImageView,
textView: TextView,
- token: Token
+ token: Token,
) {
imageView.setImageResource(R.drawable.shape_circle)
imageView.setColorFilter(token.getColor())
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt
index a792b8b25e..ab289dc6b6 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt
@@ -37,7 +37,7 @@ fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: FiatCurrenc
fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal {
val fiatValue = rateValue.multiply(this)
- return fiatValue.setScale(2, RoundingMode.DOWN)
+ return fiatValue.setScale(2, RoundingMode.HALF_UP)
}
fun BigDecimal.toFormattedFiatValue(fiatCurrencyName: FiatCurrencyName): String {
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt
index 99e6d8fe5d..f5fd832f2b 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt
@@ -1,6 +1,9 @@
package com.tangem.tap.common.extensions
-import com.tangem.tap.common.redux.AppState
+import com.tangem.tap.common.redux.StateDialog
+import com.tangem.tap.common.redux.global.GlobalAction
+import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.domain.TapError
import com.tangem.tap.scope
import com.tangem.tap.store
import kotlinx.coroutines.Dispatchers
@@ -8,8 +11,57 @@ import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Store
-fun Store.dispatchOnMain(action: Action) {
+fun Store<*>.dispatchOnMain(action: Action) {
scope.launch(Dispatchers.Main) {
store.dispatch(action)
}
-}
\ No newline at end of file
+}
+
+fun Store<*>.dispatchOpenUrl(url: String) {
+ store.dispatch(NavigationAction.OpenUrl(url))
+}
+
+fun Store<*>.dispatchNotification(resId: Int) {
+ scope.launch(Dispatchers.Main) {
+ store.dispatch(GlobalAction.ShowNotification(resId))
+ }
+}
+
+fun Store<*>.dispatchToastNotification(resId: Int) {
+ scope.launch(Dispatchers.Main) {
+ store.dispatch(GlobalAction.ShowToastNotification(resId))
+ }
+}
+
+
+fun Store<*>.dispatchErrorNotification(error: TapError) {
+ scope.launch(Dispatchers.Main) {
+ store.dispatch(GlobalAction.ShowErrorNotification(error))
+ }
+}
+
+/**
+ * @param fatal used to indicate errors that should not normally have occurred
+ */
+fun Store<*>.dispatchDebugErrorNotification(message: String, fatal: Boolean = false) {
+ val prefix = if (fatal) "FATAL ERROR: " else "DEBUG ERROR: "
+ dispatchDebugErrorNotification(TapError.CustomError("$prefix $message"))
+}
+
+fun Store<*>.dispatchDebugErrorNotification(error: TapError) {
+ scope.launch(Dispatchers.Main) {
+ store.dispatch(GlobalAction.DebugShowErrorNotification(error))
+ }
+}
+
+fun Store<*>.dispatchDialogShow(dialog: StateDialog) {
+ scope.launch(Dispatchers.Main) {
+ store.dispatch(GlobalAction.ShowDialog(dialog))
+ }
+}
+
+fun Store<*>.dispatchDialogHide() {
+ scope.launch(Dispatchers.Main) {
+ store.dispatch(GlobalAction.HideDialog)
+ }
+}
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt b/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt
new file mode 100644
index 0000000000..23d969af40
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt
@@ -0,0 +1,31 @@
+package com.tangem.tap.common.extensions
+
+import android.content.Context
+import android.view.View
+import android.widget.Toast
+import androidx.annotation.StringRes
+import androidx.fragment.app.Fragment
+
+fun Context.toast(message: String, length: Int = Toast.LENGTH_LONG) {
+ Toast.makeText(this, message, length).show()
+}
+
+fun Context.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) {
+ Toast.makeText(this, this.getString(messageRes), length).show()
+}
+
+fun View.toast(message: String, length: Int = Toast.LENGTH_LONG) {
+ context.toast(message, length)
+}
+
+fun View.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) {
+ context.toast(context.getString(messageRes), length)
+}
+
+fun Fragment.toast(message: String, length: Int = Toast.LENGTH_LONG) {
+ context?.toast(message, length)
+}
+
+fun Fragment.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) {
+ context?.let { it.toast(it.getString(messageRes), length) }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt b/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt
new file mode 100644
index 0000000000..69fbba97b6
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt
@@ -0,0 +1,24 @@
+package com.tangem.tap.common.extensions
+
+import androidx.transition.Transition
+
+/**
+[REDACTED_AUTHOR]
+ */
+inline fun Transition.addListener(
+ crossinline onStart: (animator: Transition) -> Unit = {},
+ crossinline onEnd: (animator: Transition) -> Unit = {},
+ crossinline onCancel: (animator: Transition) -> Unit = {},
+ crossinline onPause: (animator: Transition) -> Unit = {},
+ crossinline onRepeat: (animator: Transition) -> Unit = {}
+): Transition.TransitionListener {
+ val listener = object : Transition.TransitionListener {
+ override fun onTransitionStart(transition: Transition) = onStart(transition)
+ override fun onTransitionEnd(transition: Transition) = onEnd(transition)
+ override fun onTransitionCancel(transition: Transition) = onCancel(transition)
+ override fun onTransitionPause(transition: Transition) = onPause(transition)
+ override fun onTransitionResume(transition: Transition) = onRepeat(transition)
+ }
+ addListener(listener)
+ return listener
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt
index b43c49b307..e468ec2203 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt
@@ -5,6 +5,7 @@ import android.content.*
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.os.Build
+import android.os.Bundle
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
@@ -16,6 +17,7 @@ import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.google.android.material.card.MaterialCardView
+import com.tangem.common.extensions.VoidCallback
fun Fragment.getDrawable(@DrawableRes drawableResId: Int): Drawable? {
return ContextCompat.getDrawable(requireContext(), drawableResId)
@@ -30,6 +32,11 @@ fun Fragment.getColor(@ColorRes colorRes: Int): Int {
return ContextCompat.getColor(requireContext(), colorRes)
}
+@ColorInt
+fun Context.getColorCompat(@ColorRes colorRes: Int): Int {
+ return ContextCompat.getColor(this, colorRes)
+}
+
fun View.getString(@StringRes id: Int): String {
return context.getString(id)
}
@@ -71,8 +78,8 @@ fun Context.dpToPixels(dp: Int): Int =
TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), this.resources.displayMetrics
).toInt()
-tailrec fun Context?.getActivity(): Activity? = this as? Activity ?:
- (this as? ContextWrapper)?.baseContext?.getActivity()
+tailrec fun Context?.getActivity(): Activity? = this as? Activity
+ ?: (this as? ContextWrapper)?.baseContext?.getActivity()
fun MaterialCardView.setMargins(
marginLeftDp: Int = 16,
@@ -136,4 +143,19 @@ fun Context.shareText(text: String) {
fun Fragment.shareText(text: String) {
requireContext().shareText(text)
+}
+
+fun Context.safeStartActivity(
+ intent: Intent,
+ options: Bundle? = null,
+ fallback: ((ActivityNotFoundException) -> Unit)? = null,
+ finally: VoidCallback? = null
+) {
+ try {
+ this.startActivity(intent, options)
+ } catch (ex: ActivityNotFoundException) {
+ fallback?.invoke(ex)
+ } finally {
+ finally?.invoke()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt b/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt
index fb9c20bfdb..81be964cb1 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt
@@ -1,6 +1,7 @@
package com.tangem.tap.common.extensions
import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup
import android.view.ViewParent
import androidx.core.view.forEach
@@ -15,11 +16,8 @@ import timber.log.Timber
/**
[REDACTED_AUTHOR]
*/
-fun ViewGroup.inflate(viewToInflate: Int, rootView: ViewGroup?, parent: ViewGroup) {
- if (rootView == null) {
- val inflatedView = LayoutInflater.from(context).inflate(viewToInflate, rootView)
- parent.addView(inflatedView)
- }
+fun ViewGroup.inflate(viewToInflate: Int, attachToRoot: Boolean = false): View {
+ return LayoutInflater.from(context).inflate(viewToInflate, this, attachToRoot)
}
fun ViewParent?.beginDelayedTransition(transition: Transition = AutoTransition()) {
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt
new file mode 100644
index 0000000000..8f7402ff1f
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt
@@ -0,0 +1,59 @@
+package com.tangem.tap.common.extensions
+
+import com.tangem.blockchain.common.Wallet
+import com.tangem.blockchain.common.WalletManager
+import com.tangem.common.services.Result
+import com.tangem.tap.domain.TapError
+import com.tangem.tap.domain.extensions.amountToCreateAccount
+import com.tangem.tap.domain.extensions.isNoAccountError
+import com.tangem.tap.domain.getFirstToken
+import com.tangem.tap.domain.topup.TradeCryptoHelper
+import com.tangem.tap.features.wallet.redux.AddressData
+import com.tangem.tap.features.wallet.redux.reducers.createAddressesData
+import com.tangem.tap.network.NetworkConnectivity
+import com.tangem.tap.store
+
+/**
+[REDACTED_AUTHOR]
+ */
+suspend fun WalletManager.safeUpdate(): Result = try {
+ update()
+ Result.Success(wallet)
+} catch (exception: Exception) {
+ if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) {
+ Result.Failure(TapError.NoInternetConnection)
+ } else {
+ val blockchain = wallet.blockchain
+ val amountToCreateAccount = blockchain.amountToCreateAccount(wallet.getFirstToken())
+
+ if (blockchain.isNoAccountError(exception) && amountToCreateAccount != null) {
+ Result.Failure(TapError.WalletManagerUpdate.NoAccountError(amountToCreateAccount.toString()))
+ } else {
+ val message = exception.localizedMessage ?: "Unknown exception during WalletManager update"
+ Result.Failure(TapError.WalletManagerUpdate.InternalError(message))
+ }
+ }
+}
+
+fun WalletManager?.getToUpUrl(): String? {
+ val wallet = this?.wallet ?: return null
+ val config = store.state.globalState.configManager?.config ?: return null
+ val defaultAddress = wallet.address
+
+ return TradeCryptoHelper.getUrl(
+ TradeCryptoHelper.Action.Buy,
+ wallet.blockchain,
+ wallet.blockchain.currency,
+ defaultAddress,
+ config.moonPayApiKey,
+ config.moonPayApiSecretKey
+ )
+}
+
+fun WalletManager?.getAddressData(): AddressData? {
+ val wallet = this?.wallet ?: return null
+
+ val addressDataList = wallet.createAddressesData()
+ return if (addressDataList.isEmpty()) null
+ else addressDataList[0]
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/images/Picasso.kt b/app/src/main/java/com/tangem/tap/common/images/Picasso.kt
index c3b1d139a3..7d2a956bde 100644
--- a/app/src/main/java/com/tangem/tap/common/images/Picasso.kt
+++ b/app/src/main/java/com/tangem/tap/common/images/Picasso.kt
@@ -3,9 +3,12 @@ package com.tangem.tap.common.images
import android.app.Application
import com.squareup.picasso.OkHttp3Downloader
import com.squareup.picasso.Picasso
+import com.tangem.wallet.BuildConfig
import okhttp3.Cache
import okhttp3.CacheControl
import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import timber.log.Timber
import java.io.File
import java.util.concurrent.TimeUnit
@@ -17,12 +20,15 @@ class PicassoHelper {
val picasso = Picasso.Builder(application)
.downloader(OkHttp3Downloader(getOkHttpForPicasso(application)))
.build()
+ picasso.isLoggingEnabled = BuildConfig.DEBUG
Picasso.setSingletonInstance(picasso)
}
private fun getOkHttpForPicasso(application: Application): OkHttpClient {
val okHttpBuilder = OkHttpClient.Builder()
okHttpBuilder.cache(Cache(File(application.filesDir, "artworks"), Long.MAX_VALUE))
+ okHttpBuilder.callTimeout(15000, TimeUnit.MILLISECONDS)
+
okHttpBuilder.addInterceptor { chain ->
val cacheControl = CacheControl.Builder()
.maxStale(KEEP_CACHE_MAX_DAYS, TimeUnit.DAYS)
@@ -33,7 +39,25 @@ class PicassoHelper {
.build()
chain.proceed(neverExpireRequest)
}
+ addDebugInterceptors(okHttpBuilder)
+
return okHttpBuilder.build()
}
+
+ private fun addDebugInterceptors(okHttpBuilder: OkHttpClient.Builder) {
+ if (!BuildConfig.DEBUG) return
+
+ val picassoInterceptor = HttpLoggingInterceptor(PicassoOkHttpLogger()).apply {
+ level = HttpLoggingInterceptor.Level.BODY
+ }
+ okHttpBuilder.addInterceptor(picassoInterceptor)
+ }
+
+ }
+}
+
+private class PicassoOkHttpLogger : HttpLoggingInterceptor.Logger {
+ override fun log(message: String) {
+ Timber.d(message)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt
new file mode 100644
index 0000000000..a95488d44f
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt
@@ -0,0 +1,147 @@
+package com.tangem.tap.common.leapfrogWidget
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.view.View
+import android.view.animation.DecelerateInterpolator
+import android.view.animation.LinearInterpolator
+
+/**
+[REDACTED_AUTHOR]
+ */
+//fun View.setForeground(hasForegroundAtEnd: Boolean) {
+// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+// foreground = getForegroundDrawable()
+// }
+//}
+
+//fun View.getForegroundDrawable(): Drawable? {
+// return AppCompatResources.getDrawable(context, R.drawable.shape_rectangle_rounded_8_op_20)?.also {
+// it.alpha = 60
+// }
+//}
+
+typealias ProgressListener = (Int) -> Unit
+
+fun createProgressListener(duration: Long, listener: ProgressListener?): Animator {
+ val valueAnimator = ValueAnimator.ofInt(0, 100)
+ valueAnimator.duration = duration
+ valueAnimator.addUpdateListener { listener?.invoke((it.animatedValue as? Int) ?: 0) }
+ return valueAnimator
+}
+
+fun View.unfoldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet {
+ val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
+ translate.duration = animDuration
+ return AnimatorSet().apply { playTogether(translate) }
+}
+
+fun View.foldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet {
+ val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
+ translate.duration = animDuration
+
+ return AnimatorSet().apply {
+ playSequentially(translate)
+ }
+}
+
+fun View.leapAnimation(animDuration: Long, properties: LeapViewProperties, overLift: Float): AnimatorSet {
+ val halfDuration = animDuration / 2
+
+ val upTo = (height.toFloat() + overLift) * -1
+ val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo)
+ translateUp.duration = halfDuration
+ translateUp.interpolator = LinearInterpolator()
+
+ val scaleDuration = halfDuration - halfDuration / 5
+ val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
+ val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
+ val scale = AnimatorSet().apply {
+ startDelay = scaleDuration
+ duration = scaleDuration
+ interpolator = LinearInterpolator()
+ playTogether(scaleX, scaleY)
+ }
+
+ val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd, halfDuration, 100)
+ val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
+ translateDown.startDelay = halfDuration
+ translateDown.duration = halfDuration
+ translateDown.interpolator = LinearInterpolator()
+
+ return AnimatorSet().apply {
+ playTogether(translateUp, scale, elevation, translateDown)
+ }
+}
+
+fun View.leapBackAnimation(animDuration: Long, properties: LeapViewProperties, calculator: PropertyCalculator): AnimatorSet {
+ val halfDuration = animDuration / 2
+
+ val upTo = ((height).toFloat() - calculator.yTranslationFactor) * -1
+ val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo)
+ translateUp.duration = halfDuration
+ translateUp.interpolator = LinearInterpolator()
+
+ val scaleDuration = halfDuration - halfDuration / 5
+ val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
+ val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
+ val scale = AnimatorSet().apply {
+ startDelay = scaleDuration
+ duration = scaleDuration
+ interpolator = LinearInterpolator()
+ playTogether(scaleX, scaleY)
+ }
+
+ val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd, halfDuration, 100)
+ val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
+ translateDown.startDelay = halfDuration
+ translateDown.duration = halfDuration
+
+ return AnimatorSet().apply {
+ playTogether(translateUp, scale, elevation, translateDown)
+ }
+}
+
+fun View.pullUpAnimation(leapDuration: Long, properties: LeapViewProperties): AnimatorSet {
+ val pullDuration = leapDuration / 2
+ val delayDuration = leapDuration / 2
+
+ val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
+ val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
+ val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
+ val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd)
+
+ return AnimatorSet().apply {
+ startDelay = delayDuration
+ duration = pullDuration
+ interpolator = DecelerateInterpolator()
+ playTogether(translateUp, scaleX, scaleY, elevation)
+ }
+}
+
+fun View.pullDownAnimation(leapDuration: Long, properties: LeapViewProperties): AnimatorSet {
+ val pullDuration = leapDuration / 2
+ val delayDuration = leapDuration / 4
+
+ val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
+ val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
+ val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
+ val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd)
+
+ return AnimatorSet().apply {
+ startDelay = delayDuration
+ duration = pullDuration
+ interpolator = DecelerateInterpolator()
+ playTogether(translate, scaleX, scaleY, elevation)
+ }
+}
+
+fun View.elevationAnimator(start: Float, end: Float, startDelay: Long? = null, duration: Long? = null): Animator {
+ val elevationAnimator = ValueAnimator.ofFloat(start, end)
+ elevationAnimator.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: start }
+ startDelay?.let { elevationAnimator.startDelay = it }
+ duration?.let { elevationAnimator.duration = it }
+ return elevationAnimator
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt
new file mode 100644
index 0000000000..be2d63b664
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt
@@ -0,0 +1,144 @@
+package com.tangem.tap.common.leapfrogWidget
+
+import android.view.View
+
+/**
+[REDACTED_AUTHOR]
+ */
+class LeapView(
+ val view: View,
+ val index: Int,
+ position: Int,
+ private val maximalPosition: Int,
+ private val calculator: PropertyCalculator,
+) {
+ var state: LeapViewState
+ private set
+
+ val initialState: LeapViewState
+
+ init {
+ val initialProperties = createInitialProperty(position)
+ state = LeapViewState(index, position, position, initialProperties)
+ initialState = state.copy()
+ }
+
+ fun initView() {
+ applyState(initialState)
+ }
+
+ fun leap(): LeapFrogAnimation {
+ val previousPosition = state.currentPosition
+ val currentPosition = when (previousPosition) {
+ 0 -> maximalPosition
+ else -> state.currentPosition - 1
+ }
+
+ state = state.copy(
+ currentPosition = currentPosition,
+ previousPosition = previousPosition,
+ properties = createLeapAnimationProperty(previousPosition, currentPosition)
+ )
+
+ return when {
+ previousPosition == 0 && currentPosition == maximalPosition -> LeapFrogAnimation.LEAP
+ else -> LeapFrogAnimation.PULL
+ }
+ }
+
+ fun leapBack(): LeapFrogAnimation {
+ val previousPosition = state.currentPosition
+ val currentPosition = when (previousPosition) {
+ maximalPosition -> 0
+ else -> state.currentPosition + 1
+ }
+ state = state.copy(
+ currentPosition = currentPosition,
+ previousPosition = previousPosition,
+ properties = createLeapAnimationProperty(previousPosition, currentPosition)
+ )
+
+ return when {
+ previousPosition == maximalPosition && currentPosition == 0 -> LeapFrogAnimation.LEAP
+ else -> LeapFrogAnimation.PULL
+ }
+ }
+
+ fun fold() {
+ state = state.copy(properties = state.properties.toFold()
+ )
+ }
+
+ fun unfold() {
+ state = state.copy(properties = state.properties.toUnfold(calculator))
+ }
+
+ fun applyState(state: LeapViewState) {
+ this.state = state
+ setProperties(this.state.properties)
+ }
+
+ private fun setProperties(properties: LeapViewProperties) {
+ view.elevation = properties.elevationEnd
+ view.translationY = properties.yTranslation
+ view.scaleX = properties.scale
+ view.scaleY = properties.scale
+// initialProperties.hasForeground
+ }
+
+ private fun createInitialProperty(endPosition: Int): LeapViewProperties {
+ return LeapViewProperties(
+ endPosition,
+ endPosition,
+ calculator.scale(endPosition),
+ calculator.elevation(endPosition, maximalPosition + 1),
+ calculator.elevation(endPosition, maximalPosition + 1),
+ calculator.yTranslation(0),
+ calculator.hasForeground(endPosition),
+ )
+ }
+
+ private fun createLeapAnimationProperty(startPosition: Int, endPosition: Int): LeapViewProperties {
+ return LeapViewProperties(
+ startPosition,
+ endPosition,
+ calculator.scale(endPosition),
+ calculator.elevation(startPosition, maximalPosition + 1),
+ calculator.elevation(endPosition, maximalPosition + 1),
+ calculator.yTranslation(endPosition),
+ calculator.hasForeground(endPosition),
+ )
+ }
+
+ private fun LeapViewProperties.toUnfold(calculator: PropertyCalculator): LeapViewProperties {
+ return this.copy(yTranslation = calculator.yTranslation(this.positionEnd))
+ }
+
+ private fun LeapViewProperties.toFold(): LeapViewProperties {
+ return this.copy(yTranslation = 0f)
+ }
+
+ override fun toString(): String = "index: $index, position: ${state.currentPosition}, code: ${view.hashCode()}"
+
+}
+
+data class LeapViewState(
+ val index: Int,
+ val currentPosition: Int,
+ val previousPosition: Int,
+ val properties: LeapViewProperties
+)
+
+enum class LeapFrogAnimation {
+ LEAP, PULL
+}
+
+data class LeapViewProperties(
+ val positionStart: Int,
+ val positionEnd: Int,
+ val scale: Float,
+ val elevationStart: Float,
+ val elevationEnd: Float,
+ val yTranslation: Float,
+ val hasForeground: Boolean,
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt
new file mode 100644
index 0000000000..884cef3f94
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt
@@ -0,0 +1,258 @@
+package com.tangem.tap.common.leapfrogWidget
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.view.animation.AccelerateDecelerateInterpolator
+import android.widget.FrameLayout
+import androidx.core.animation.doOnEnd
+import androidx.core.view.children
+import com.tangem.tangem_sdk_new.extensions.dpToPx
+
+/**
+[REDACTED_AUTHOR]
+ */
+class LeapfrogWidget(
+ private val parentContainer: FrameLayout,
+ private val calculator: PropertyCalculator = PropertyCalculator(),
+) {
+ private val foldAnimationDuration = 500L
+ private val leapAnimationDuration = 800L
+ private val leapBackAnimationDuration = 800L
+
+ private val leapViews = mutableListOf()
+
+ private var foldUnfoldProgress: Int = 100
+ private var leapProgress: Int = 100
+ private var leapBackProgress: Int = 100
+
+ var isFolded: Boolean = true
+ private set
+
+ init {
+ calculator.setTranslationConverter { parentContainer.dpToPx(it) }
+ if (viewIsFullFledged()) {
+ val maxPosition = parentContainer.childCount - 1
+ parentContainer.children.forEachIndexed { index, view ->
+ val initialPosition = maxPosition - index
+ leapViews.add(LeapView(view, index, initialPosition, maxPosition, calculator))
+ }
+ }
+ }
+
+ /**
+ * Apply initial properties to the views
+ */
+ fun initViews() {
+ isFolded = true
+ leapViews.forEach { it.initView() }
+ }
+
+ fun fold(onEnd: () -> Unit = {}) {
+ val animator = foldAnimator()
+ if (animator == null) {
+ onEnd()
+ } else {
+ animator.doOnEnd { onEnd() }
+ animator.start()
+ }
+ }
+
+ fun unfold(onEnd: () -> Unit = {}) {
+ val animator = unfoldAnimator()
+ if (animator == null) {
+ onEnd()
+ } else {
+ animator.doOnEnd { onEnd() }
+ animator.start()
+ }
+ }
+
+ fun foldAnimator(): Animator? {
+ if (!canFoldUnfold() || isFolded) return null
+
+ return createFoldUnfoldAnimator(true, foldAnimationDuration)
+ }
+
+ fun unfoldAnimator(): Animator? {
+ if (!canFoldUnfold() || !isFolded) return null
+
+ return createFoldUnfoldAnimator(false, foldAnimationDuration)
+ }
+
+ private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, duration: Long): Animator {
+ val animatorsList = mutableListOf()
+ leapViews.forEach {
+ if (isFoldAnimation) {
+ it.fold()
+ animatorsList.add(it.view.foldAnimation(duration, it.state.properties))
+ } else {
+ it.unfold()
+ animatorsList.add(it.view.unfoldAnimation(duration, it.state.properties))
+ }
+ }
+ animatorsList.add(createProgressListener(duration) {
+ foldUnfoldProgress = it
+ if (it == 100) isFolded = isFoldAnimation
+ })
+
+ val animator = AnimatorSet()
+ animator.interpolator = AccelerateDecelerateInterpolator()
+ animator.playTogether(animatorsList.toList())
+ return animator
+ }
+
+ fun leap(onEnd: () -> Unit = {}) {
+ if (!canLeap()) {
+ onEnd()
+ return
+ }
+
+ val duration = leapAnimationDuration
+ val animatorsList = mutableListOf()
+ leapViews.forEach {
+ when (it.leap()) {
+ LeapFrogAnimation.LEAP -> {
+ val overLift = calculator.overLift(leapViews.size)
+ animatorsList.add(it.view.leapAnimation(duration, it.state.properties, overLift))
+ }
+ LeapFrogAnimation.PULL -> {
+ animatorsList.add(it.view.pullUpAnimation(duration, it.state.properties))
+ }
+ }
+ }
+ animatorsList.add(createProgressListener(duration) { leapProgress = it })
+
+ val animator = AnimatorSet()
+ animator.playTogether(animatorsList.toList())
+ animator.doOnEnd { onEnd() }
+ animator.start()
+ }
+
+ fun leapBack(onEnd: () -> Unit = {}) {
+ if (!canLeapBack()) {
+ onEnd()
+ return
+ }
+
+ val duration = leapBackAnimationDuration
+ val animatorsList = mutableListOf()
+ leapViews.forEach {
+ when (it.leapBack()) {
+ LeapFrogAnimation.LEAP -> {
+ animatorsList.add(it.view.leapBackAnimation(duration, it.state.properties, calculator))
+ }
+ LeapFrogAnimation.PULL -> {
+ animatorsList.add(it.view.pullDownAnimation(duration, it.state.properties))
+ }
+ }
+ }
+ animatorsList.add(createProgressListener(duration) { leapBackProgress = it })
+
+ val animator = AnimatorSet()
+ animator.playTogether(animatorsList.toList())
+ animator.doOnEnd { onEnd() }
+ animator.start()
+ }
+
+ fun getViewsCount(): Int = parentContainer.childCount
+
+ fun getViewPositionByIndex(index: Int): Int = leapViews.first { it.index == index }.state.currentPosition
+
+ fun getViewByPosition(position: Int): LeapView = leapViews.first { it.state.currentPosition == position }
+
+ fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(isFolded, leapViews.map { it.state })
+
+ fun applyState(state: LeapfrogWidgetState) {
+ isFolded = state.isFolded
+ state.leapViewStates.forEach { state ->
+ leapViews.firstOrNull { it.index == state.index }?.applyState(state)
+ }
+ }
+
+ private fun viewIsFullFledged(): Boolean = parentContainer.childCount > 1
+
+ private fun canFoldUnfold(): Boolean {
+ return when {
+ !viewIsFullFledged() -> false
+ foldUnfoldInProgress() -> false
+ leapInProgress() || leapBackInProgress() -> false
+ else -> true
+ }
+ }
+
+ private fun canLeap(): Boolean {
+ return when {
+ !viewIsFullFledged() -> false
+ isFolded -> false
+ leapBackInProgress() -> false
+ leapProgress < 70 -> false
+ else -> true
+ }
+ }
+
+ private fun canLeapBack(): Boolean {
+ return when {
+ !viewIsFullFledged() -> false
+ isFolded -> false
+ leapInProgress() -> false
+ leapBackProgress < 70 -> false
+ else -> true
+ }
+ }
+
+ private fun foldUnfoldInProgress(): Boolean = foldUnfoldProgress != 100
+ private fun leapInProgress(): Boolean = leapProgress != 100
+ private fun leapBackInProgress(): Boolean = leapBackProgress != 100
+}
+
+data class LeapfrogWidgetState(
+ val isFolded: Boolean,
+ val leapViewStates: List
+)
+
+class PropertyCalculator(
+ val elevationFactor: Float = 1f,
+ val decreaseScaleFactor: Float = 0.1f,
+ val yTranslationFactor: Float = 20f,
+ val decreaseOverLiftingFactor: Float = 0.85f,
+) {
+ // 0 view it is view on the top of stack, but it is last in parent.children
+
+ private var translationValueConverter: ((Float) -> Float)? = null
+ fun setTranslationConverter(converter: (Float) -> Float) {
+ translationValueConverter = converter
+ }
+
+ fun scale(position: Int): Float {
+ return 1f - decreaseScaleFactor * position
+ }
+
+ fun elevation(position: Int, count: Int): Float {
+ return elevationFactor * (count - 1 - position)
+ }
+
+ fun yTranslation(position: Int): Float {
+ val yTranslation = yTranslationFactor * position
+ return convertTranslation(yTranslation)
+ }
+
+ fun hasForeground(position: Int): Boolean {
+ return position != 0
+ }
+
+ fun overLift(viewCount: Int): Float {
+ val initialOverLift = when {
+ yTranslationFactor < 0f -> yTranslationFactor * decreaseOverLiftingFactor * viewCount * -1
+ else -> 10f
+ }
+ val scaleOverLift = when {
+ decreaseScaleFactor == 0f -> initialOverLift
+ else -> initialOverLift * decreaseOverLiftingFactor - initialOverLift * decreaseScaleFactor
+ }
+ return convertTranslation(scaleOverLift)
+ }
+
+ private fun convertTranslation(value: Float): Float {
+ return translationValueConverter?.invoke(value) ?: value
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt
new file mode 100644
index 0000000000..1a41ff35be
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt
@@ -0,0 +1,68 @@
+package com.tangem.tap.common.leapfrogWidget
+
+import android.os.Bundle
+import android.view.View
+import android.widget.FrameLayout
+import androidx.fragment.app.Fragment
+import androidx.transition.TransitionInflater
+import com.tangem.tap.domain.twins.TwinsCardWidget
+import com.tangem.wallet.R
+import kotlinx.android.synthetic.main.test_leapfrog_fragment.*
+
+class TestLeapfrogFragment : Fragment(R.layout.test_leapfrog_fragment) {
+
+ private lateinit var twinsCardWidget: TwinsCardWidget
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val inflater = TransitionInflater.from(requireContext())
+ exitTransition = inflater.inflateTransition(R.transition.fade)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ val leapfrogContainer: FrameLayout = view.findViewById(R.id.leapfrog_views_container)
+ val leapfrog = LeapfrogWidget(leapfrogContainer)
+ twinsCardWidget = TwinsCardWidget(leapfrog) { 200f }
+
+ btn_twin_welcome.setOnClickListener {
+ twinsCardWidget.toWelcome()
+ }
+ btn_twin_to_leapfrog.setOnClickListener {
+ twinsCardWidget.toLeapfrog()
+ }
+ btn_twin_activate.setOnClickListener {
+ twinsCardWidget.toActivate()
+ }
+
+
+ btn_lp_init.setOnClickListener {
+ leapfrog.initViews()
+ }
+ btn_lp_unfold.setOnClickListener {
+ leapfrog.unfold()
+ }
+ btn_lp_fold.setOnClickListener {
+ leapfrog.fold()
+ }
+ btn_lp_leap.setOnClickListener {
+ leapfrog.leap()
+ }
+ btn_lp_leap_back.setOnClickListener {
+ leapfrog.leapBack()
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ leapfrogWidgetState?.let { twinsCardWidget.leapfrogWidget.applyState(it) }
+ }
+
+ override fun onStop() {
+ super.onStop()
+ leapfrogWidgetState = twinsCardWidget.leapfrogWidget.getState()
+ }
+}
+
+private var leapfrogWidgetState: LeapfrogWidgetState? = null
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt
index 57ed1eaf2c..38c76819b3 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt
@@ -6,6 +6,9 @@ import com.tangem.tap.features.details.redux.DetailsReducer
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer
import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer
import com.tangem.tap.features.home.redux.HomeReducer
+import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer
+import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsReducer
+import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer
import com.tangem.tap.features.send.redux.reducers.SendScreenReducer
import com.tangem.tap.features.tokens.redux.TokensReducer
import com.tangem.tap.features.wallet.redux.reducers.WalletReducer
@@ -19,6 +22,9 @@ fun appReducer(action: Action, state: AppState?): AppState {
navigationState = NavigationReducer.reduce(action, state),
globalState = globalReducer(action, state),
homeState = HomeReducer.reduce(action, state),
+ onboardingNoteState = OnboardingNoteReducer.reduce(action, state),
+ onboardingWalletState = OnboardingWalletReducer.reduce(action, state),
+ onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state),
walletState = WalletReducer.reduce(action, state),
sendState = SendScreenReducer.reduce(action, state.sendState),
detailsState = DetailsReducer.reduce(action, state),
diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt
index 02deb7d980..40e0c80077 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt
@@ -1,7 +1,7 @@
package com.tangem.tap.common.redux
+import com.tangem.tap.common.redux.global.GlobalMiddleware
import com.tangem.tap.common.redux.global.GlobalState
-import com.tangem.tap.common.redux.global.globalMiddleware
import com.tangem.tap.common.redux.navigation.NavigationState
import com.tangem.tap.common.redux.navigation.navigationMiddleware
import com.tangem.tap.features.details.redux.DetailsMiddleware
@@ -12,7 +12,13 @@ import com.tangem.tap.features.disclaimer.redux.DisclaimerMiddleware
import com.tangem.tap.features.disclaimer.redux.DisclaimerState
import com.tangem.tap.features.home.redux.HomeMiddleware
import com.tangem.tap.features.home.redux.HomeState
-import com.tangem.tap.features.send.redux.middlewares.sendMiddleware
+import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMiddleware
+import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState
+import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsMiddleware
+import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState
+import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware
+import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
+import com.tangem.tap.features.send.redux.middlewares.SendMiddleware
import com.tangem.tap.features.send.redux.states.SendState
import com.tangem.tap.features.tokens.redux.TokensMiddleware
import com.tangem.tap.features.tokens.redux.TokensState
@@ -25,6 +31,9 @@ data class AppState(
val navigationState: NavigationState = NavigationState(),
val globalState: GlobalState = GlobalState(),
val homeState: HomeState = HomeState(),
+ val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(),
+ val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(),
+ val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(),
val walletState: WalletState = WalletState(),
val sendState: SendState = SendState(),
val detailsState: DetailsState = DetailsState(),
@@ -36,10 +45,14 @@ data class AppState(
companion object {
fun getMiddleware(): List> {
return listOf(
- logMiddleware, navigationMiddleware, notificationsMiddleware, globalMiddleware,
- HomeMiddleware().homeMiddleware,
+ logMiddleware, navigationMiddleware, notificationsMiddleware,
+ GlobalMiddleware.handler,
+ HomeMiddleware.handler,
+ OnboardingNoteMiddleware.handler,
+ OnboardingWalletMiddleware.handler,
+ OnboardingOtherCardsMiddleware.handler,
WalletMiddleware().walletMiddleware,
- sendMiddleware,
+ SendMiddleware().sendMiddleware,
DetailsMiddleware().detailsMiddleware,
DisclaimerMiddleware().disclaimerMiddleware,
TokensMiddleware().tokensMiddleware,
diff --git a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt
index 8a55bcfafe..2ace551205 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt
@@ -9,6 +9,7 @@ import com.tangem.tap.domain.MultiMessageError
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.assembleErrors
import com.tangem.tap.notificationsHandler
+import com.tangem.wallet.BuildConfig
import org.rekotlin.Action
import org.rekotlin.Middleware
import java.lang.ref.WeakReference
@@ -63,23 +64,29 @@ fun getMessageString(context: Context, message: Int, args: List?): String {
val notificationsMiddleware: Middleware = { dispatch, state ->
{ next ->
{ action ->
- when (action) {
- is NotificationAction -> notificationsHandler?.showNotification(action.messageResource)
- is ToastNotificationAction -> notificationsHandler?.showToastNotification(action.messageResource)
- is ErrorAction -> {
- when (action.error) {
- is MultiMessageError -> {
- val multiError = action.error as MultiMessageError
- notificationsHandler?.showNotification(multiError.assembleErrors(), multiError.builder)
- }
- else -> {
- val args = (action.error as? ArgError)?.args ?: listOf()
- notificationsHandler?.showNotification(action.error.localizedMessage, args)
- }
- }
+ handleNotificationAction(action)
+ next(action)
+ }
+ }
+}
+
+private fun handleNotificationAction(action: Action) {
+ if (action is Debug && !BuildConfig.DEBUG) return
+
+ when (action) {
+ is NotificationAction -> notificationsHandler?.showNotification(action.messageResource)
+ is ToastNotificationAction -> notificationsHandler?.showToastNotification(action.messageResource)
+ is ErrorAction -> {
+ when (action.error) {
+ is MultiMessageError -> {
+ val multiError = action.error as MultiMessageError
+ notificationsHandler?.showNotification(multiError.assembleErrors(), multiError.builder)
+ }
+ else -> {
+ val args = (action.error as? ArgError)?.args ?: listOf()
+ notificationsHandler?.showNotification(action.error.messageResource, args)
}
}
- next(action)
}
}
}
@@ -94,4 +101,10 @@ interface NotificationAction : Action {
interface ErrorAction : Action {
val error: TapError
-}
\ No newline at end of file
+}
+
+// Processed only in the debug builds
+interface Debug
+interface DebugNotification : Debug, NotificationAction
+interface DebugToastNotification : Debug, ToastNotificationAction
+interface DebugErrorAction : Debug, ErrorAction
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt
new file mode 100644
index 0000000000..36e1a749a5
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt
@@ -0,0 +1,17 @@
+package com.tangem.tap.common.redux
+
+import com.tangem.tap.features.wallet.redux.AddressData
+
+/**
+[REDACTED_AUTHOR]
+ */
+interface StateDialog
+
+sealed class AppDialog : StateDialog {
+ object ScanFailsDialog : AppDialog()
+ data class AddressInfoDialog(
+ val addressData: AddressData,
+ val onCopyAddress: () -> Unit,
+ val onExploreAddress: () -> Unit
+ ) : AppDialog()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt
index 419fa5737d..297ba9fd40 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt
@@ -2,24 +2,52 @@ package com.tangem.tap.common.redux.global
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.CompletionResult
+import com.tangem.common.core.TangemError
+import com.tangem.tap.common.redux.*
+import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.config.ConfigManager
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
-import com.tangem.tap.domain.tasks.ScanNoteResponse
+import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.features.details.redux.SecurityOption
import com.tangem.tap.features.feedback.EmailData
import com.tangem.tap.features.feedback.FeedbackManager
+import com.tangem.tap.network.moonpay.MoonPayUserStatus
import org.rekotlin.Action
sealed class GlobalAction : Action {
+ // notifications
+ data class ShowNotification(override val messageResource: Int) : GlobalAction(), NotificationAction
+ data class ShowToastNotification(override val messageResource: Int) : GlobalAction(), ToastNotificationAction
+ data class ShowErrorNotification(override val error: TapError) : GlobalAction(), ErrorAction
+ data class DebugShowErrorNotification(override val error: TapError) : GlobalAction(), DebugErrorAction
+
+ // dialogs
+ data class ShowDialog(val stateDialog: StateDialog) : GlobalAction()
+ object HideDialog : GlobalAction()
+
+ sealed class Onboarding {
+ data class Start(val scanResponse: ScanResponse) : GlobalAction()
+ object Stop : GlobalAction()
+ }
+
+ data class ScanCard(
+ val onSuccess: ((ScanResponse) -> Unit)? = null,
+ val onFailure: ((TangemError) -> Unit)? = null,
+ val messageResId: Int? = null,
+ ) : GlobalAction()
+
object ScanFailsCounter {
- data class ChooseBehavior(val result: CompletionResult) : GlobalAction()
+ data class ChooseBehavior(val result: CompletionResult) : GlobalAction()
object Reset : GlobalAction()
object Increment : GlobalAction()
}
- data class SaveScanNoteResponse(val scanNoteResponse: ScanNoteResponse) : GlobalAction()
+ data class SaveScanNoteResponse(val scanResponse: ScanResponse) : GlobalAction()
+
+ data class SetIfCardVerifiedOnline(val verified: Boolean) : GlobalAction()
+
data class ChangeAppCurrency(val appCurrency: FiatCurrencyName) : GlobalAction()
object RestoreAppCurrency : GlobalAction() {
data class Success(val appCurrency: FiatCurrencyName) : GlobalAction()
@@ -41,6 +69,7 @@ sealed class GlobalAction : Action {
data class SendFeedback(val emailData: EmailData) : GlobalAction()
data class UpdateFeedbackInfo(val walletManagers: List) : GlobalAction()
- data class ShowDialog(val stateDialog: StateDialog) : GlobalAction()
- object HideDialog : GlobalAction()
+ object GetMoonPayUserStatus : GlobalAction() {
+ data class Success(val moonPayUserStatus: MoonPayUserStatus) : GlobalAction()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt
index 2f79a5bbcc..ea80ab463c 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt
@@ -1,17 +1,32 @@
package com.tangem.tap.common.redux.global
-import com.tangem.TangemSdkError
import com.tangem.common.CompletionResult
+import com.tangem.common.core.TangemSdkError
+import com.tangem.common.services.Result
+import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
+import com.tangem.tap.common.extensions.dispatchDialogShow
+import com.tangem.tap.common.extensions.dispatchOnMain
+import com.tangem.tap.common.extensions.withMainContext
+import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
-import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.features.wallet.redux.WalletAction
+import com.tangem.tap.network.moonpay.MoonpayService
import com.tangem.tap.preferencesStorage
+import com.tangem.tap.scope
import com.tangem.tap.store
+import com.tangem.tap.tangemSdkManager
+import kotlinx.coroutines.launch
import org.rekotlin.Middleware
-val globalMiddleware: Middleware = { dispatch, appState ->
+class GlobalMiddleware {
+ companion object {
+ val handler = globalMiddlewareHandler
+ }
+}
+
+private val globalMiddlewareHandler: Middleware = { dispatch, appState ->
{ nextDispatch ->
{ action ->
when (action) {
@@ -22,8 +37,7 @@ val globalMiddleware: Middleware = { dispatch, appState ->
if (action.result.error is TangemSdkError.UserCancelled) {
store.dispatch(GlobalAction.ScanFailsCounter.Increment)
if (store.state.globalState.scanCardFailsCounter >= 2) {
- store.dispatch(HomeAction.ShowDialog.ScanFails)
- store.dispatch(WalletAction.ShowDialog.ScanFails)
+ store.dispatchDialogShow(AppDialog.ScanFailsDialog)
}
} else {
store.dispatch(GlobalAction.ScanFailsCounter.Reset)
@@ -58,7 +72,35 @@ val globalMiddleware: Middleware = { dispatch, appState ->
}
is GlobalAction.UpdateFeedbackInfo -> {
store.state.globalState.feedbackManager?.infoHolder
- ?.setWalletsInfo(action.walletManagers)
+ ?.setWalletsInfo(action.walletManagers)
+ }
+ is GlobalAction.GetMoonPayUserStatus -> {
+ val apiKey = appState()?.globalState?.configManager?.config?.moonPayApiKey
+ if (apiKey != null) {
+ scope.launch {
+ val userStatusResponse = MoonpayService().getUserStatus(apiKey)
+ if (userStatusResponse is Result.Success) {
+ store.dispatchOnMain(
+ GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data)
+ )
+ }
+ }
+ }
+ }
+ is GlobalAction.ScanCard -> {
+ scope.launch {
+ val result = tangemSdkManager.scanProduct(FirebaseAnalyticsHandler, action.messageResId)
+ store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result))
+ withMainContext {
+ when (result) {
+ is CompletionResult.Success -> {
+ tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card)
+ action.onSuccess?.invoke(result.data)
+ }
+ is CompletionResult.Failure -> action.onFailure?.invoke(result.error)
+ }
+ }
+ }
}
}
nextDispatch(action)
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt
index b7208034f7..29ec84876e 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt
@@ -1,8 +1,8 @@
package com.tangem.tap.common.redux.global
-import com.tangem.commands.wallet.WalletIndex
import com.tangem.tap.common.redux.AppState
-import com.tangem.tap.features.details.redux.SecurityOption
+import com.tangem.tap.features.onboarding.OnboardingManager
+import com.tangem.tap.preferencesStorage
import org.rekotlin.Action
fun globalReducer(action: Action, state: AppState): GlobalState {
@@ -12,6 +12,14 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
val globalState = state.globalState
return when (action) {
+ is GlobalAction.Onboarding.Start -> {
+ val usedCardsPrefStorage = preferencesStorage.usedCardsPrefStorage
+ val onboardingState = OnboardingManager(action.scanResponse, usedCardsPrefStorage)
+ globalState.copy(onboardingManager = onboardingState)
+ }
+ is GlobalAction.Onboarding.Stop -> {
+ globalState.copy(onboardingManager = null)
+ }
is GlobalAction.ScanFailsCounter.Increment -> {
globalState.copy(scanCardFailsCounter = globalState.scanCardFailsCounter + 1)
}
@@ -19,7 +27,7 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
globalState.copy(scanCardFailsCounter = 0)
}
is GlobalAction.SaveScanNoteResponse ->
- globalState.copy(scanNoteResponse = action.scanNoteResponse)
+ globalState.copy(scanResponse = action.scanResponse)
is GlobalAction.ChangeAppCurrency -> {
globalState.copy(appCurrency = action.appCurrency)
}
@@ -30,36 +38,18 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
globalState.copy(configManager = action.configManager)
}
is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager)
- is GlobalAction.UpdateSecurityOptions -> {
- val card = when (action.securityOption) {
- SecurityOption.LongTap -> globalState.scanNoteResponse?.card?.copy(
- isPin1Default = true, isPin2Default = true
- )
- SecurityOption.PassCode -> globalState.scanNoteResponse?.card?.copy(
- isPin1Default = true, isPin2Default = false
- )
- SecurityOption.AccessCode -> globalState.scanNoteResponse?.card?.copy(
- isPin1Default = false, isPin2Default = true
- )
- }
- if (card != null) {
- globalState.copy(scanNoteResponse = globalState.scanNoteResponse?.copy(card = card))
- } else {
- globalState
- }
- }
is GlobalAction.UpdateWalletSignedHashes -> {
- val wallet = globalState.scanNoteResponse?.card
- ?.wallet(WalletIndex.PublicKey(action.walletPublicKey))
- ?.copy(
- signedHashes = action.walletSignedHashes,
- remainingSignatures = action.remainingSignatures
- )
- val card = globalState.scanNoteResponse?.card
- wallet?.let { globalState.scanNoteResponse.card.updateWallet(wallet) }
+ val wallet = globalState.scanResponse?.card
+ ?.wallet(action.walletPublicKey)
+ ?.copy(
+ totalSignedHashes = action.walletSignedHashes,
+ remainingSignatures = action.remainingSignatures
+ )
+ val card = globalState.scanResponse?.card
+ wallet?.let { globalState.scanResponse.card.updateWallet(wallet) }
if (card != null) {
- globalState.copy(scanNoteResponse = globalState.scanNoteResponse.copy(card = card))
+ globalState.copy(scanResponse = globalState.scanResponse.copy(card = card))
} else {
globalState
}
@@ -73,6 +63,12 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
is GlobalAction.HideDialog -> {
globalState.copy(dialog = null)
}
+ is GlobalAction.GetMoonPayUserStatus.Success -> {
+ globalState.copy(moonPayUserStatus = action.moonPayUserStatus)
+ }
+ is GlobalAction.SetIfCardVerifiedOnline ->
+ globalState.copy(cardVerifiedOnline = action.verified)
+
else -> globalState
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt
index c7e173310b..e27641b854 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt
@@ -1,32 +1,33 @@
package com.tangem.tap.common.redux.global
-import com.tangem.commands.common.network.TangemService
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
+import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.domain.PayIdManager
import com.tangem.tap.domain.TapWalletManager
import com.tangem.tap.domain.configurable.config.ConfigManager
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
-import com.tangem.tap.domain.tasks.ScanNoteResponse
+import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.features.feedback.FeedbackManager
+import com.tangem.tap.features.onboarding.OnboardingManager
import com.tangem.tap.network.coinmarketcap.CoinMarketCapService
+import com.tangem.tap.network.moonpay.MoonPayUserStatus
import org.rekotlin.StateType
data class GlobalState(
- val scanNoteResponse: ScanNoteResponse? = null,
- val tapWalletManager: TapWalletManager = TapWalletManager(),
- val payIdManager: PayIdManager = PayIdManager(),
- val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(),
- val tangemService: TangemService = TangemService(),
- val configManager: ConfigManager? = null,
- val warningManager: WarningMessagesManager? = null,
- val feedbackManager: FeedbackManager? = null,
- val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY,
- val scanCardFailsCounter: Int = 0,
- val dialog: StateDialog? = null
+ val scanResponse: ScanResponse? = null,
+ val onboardingManager: OnboardingManager? = null,
+ val cardVerifiedOnline: Boolean = false,
+ val tapWalletManager: TapWalletManager = TapWalletManager(),
+ val payIdManager: PayIdManager = PayIdManager(),
+ val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(),
+ val configManager: ConfigManager? = null,
+ val warningManager: WarningMessagesManager? = null,
+ val feedbackManager: FeedbackManager? = null,
+ val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY,
+ val scanCardFailsCounter: Int = 0,
+ val dialog: StateDialog? = null,
+ val moonPayUserStatus: MoonPayUserStatus? = null,
) : StateType
typealias CryptoCurrencyName = String
-typealias FiatCurrencyName = String
-
-
-interface StateDialog
\ No newline at end of file
+typealias FiatCurrencyName = String
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt
index 5cbdb915f6..331dc07e1a 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt
@@ -1,15 +1,30 @@
package com.tangem.tap.common.redux.navigation
+import android.view.View
import androidx.fragment.app.FragmentActivity
+import androidx.transition.TransitionSet
import org.rekotlin.Action
import java.lang.ref.WeakReference
sealed class NavigationAction : Action {
- data class NavigateTo(val screen: AppScreen, val addToBackstack: Boolean = true) :
- NavigationAction()
+ data class NavigateTo(
+ val screen: AppScreen,
+ val fragmentShareTransition: FragmentShareTransition? = null,
+ val addToBackstack: Boolean = true
+ ) : NavigationAction()
data class PopBackTo(val screen: AppScreen? = null) : NavigationAction()
+ data class OpenUrl(val url: String) : NavigationAction()
+
data class ActivityCreated(val activity: WeakReference) : NavigationAction()
object ActivityDestroyed : NavigationAction()
-}
\ No newline at end of file
+}
+
+data class FragmentShareTransition(
+ val shareElements: List,
+ val enterTransitionSet: TransitionSet,
+ val exitTransitionSet: TransitionSet
+)
+
+data class ShareElement(val wView: WeakReference, val name: String)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt
index 8c4d5e9fea..31fe730615 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt
@@ -1,25 +1,34 @@
package com.tangem.tap.common.redux.navigation
+import com.tangem.tap.common.CustomTabsManager
import com.tangem.tap.common.extensions.openFragment
import com.tangem.tap.common.extensions.popBackTo
import com.tangem.tap.common.redux.AppState
-import com.tangem.tap.store
import org.rekotlin.Middleware
val navigationMiddleware: Middleware = { dispatch, state ->
{ next ->
{ action ->
if (action is NavigationAction) {
- val navState = store.state.navigationState
+ val navState = state()?.navigationState
when (action) {
is NavigationAction.NavigateTo -> {
- navState.activity?.get()?.openFragment(action.screen, action.addToBackstack)
+ navState?.activity?.get()?.openFragment(
+ action.screen,
+ action.addToBackstack,
+ action.fragmentShareTransition
+ )
}
is NavigationAction.PopBackTo -> {
if (action.screen == AppScreen.Home) {
- navState.activity?.get()?.popBackTo(null, true)
+ navState?.activity?.get()?.popBackTo(null, true)
} else {
- navState.activity?.get()?.popBackTo(action.screen)
+ navState?.activity?.get()?.popBackTo(action.screen)
+ }
+ }
+ is NavigationAction.OpenUrl -> {
+ navState?.activity?.get()?.let {
+ CustomTabsManager().openUrl(action.url, it)
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt
index a4ce784435..80cd5e3260 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt
@@ -27,5 +27,6 @@ private fun internalReduce(action: Action, state: AppState): NavigationState {
}
is NavigationAction.ActivityCreated -> navState.copy(activity = navigationAction.activity)
is NavigationAction.ActivityDestroyed -> navState.copy(activity = null)
+ else -> navState
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt
index ed59b9b2d5..4fa02150f0 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt
@@ -5,12 +5,19 @@ import org.rekotlin.StateType
import java.lang.ref.WeakReference
data class NavigationState(
- val backStack: List = listOf(AppScreen.Home),
- val activity: WeakReference? = null
+ val backStack: List = listOf(AppScreen.Home),
+ val activity: WeakReference? = null
) : StateType
enum class AppScreen {
- Home, Wallet, WalletDetails, Send, Details, DetailsConfirm, DetailsSecurity, Disclaimer,
- CreateTwinWalletWarning, CreateTwinWallet, TwinsOnboarding, AddTokens, WalletConnectSessions,
+ Home,
+ Disclaimer,
+ OnboardingNote, OnboardingWallet, OnboardingTwins, OnboardingOther,
+ Wallet, WalletDetails,
+ Send,
+ Details, DetailsConfirm, DetailsSecurity,
+ CreateTwinWalletWarning, CreateTwinWallet,
+ AddTokens,
+ WalletConnectSessions,
QrScan
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt
index c6cb5f22bd..b4eacfff4e 100644
--- a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt
+++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt
@@ -5,6 +5,7 @@ import android.view.View
import com.google.android.material.button.MaterialButton
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
+import com.tangem.tap.features.wallet.redux.ProgressState
/**
[REDACTED_AUTHOR]
@@ -12,7 +13,7 @@ import com.tangem.tap.common.extensions.show
open class IndeterminateProgressButtonWidget(
private val button: MaterialButton,
private val progress: View,
- initialState: ProgressState = ProgressState.None
+ initialState: ProgressState = ProgressState.Done
) : ViewStateWidget {
private var initialButtonText: CharSequence = button.text
@@ -23,12 +24,14 @@ open class IndeterminateProgressButtonWidget(
changeState(initialState)
}
+ override val mainView: View = button
+
override fun changeState(state: WidgetState) {
val progressState = state as? ProgressState ?: return
when (progressState) {
- is ProgressState.None -> switchToNone()
- is ProgressState.Progress -> switchToProgress()
+ ProgressState.Done, ProgressState.Error -> switchToNone()
+ ProgressState.Loading -> switchToProgress()
}
}
diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt
new file mode 100644
index 0000000000..e7ef08e026
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt
@@ -0,0 +1,100 @@
+package com.tangem.tap.common.toggleWidget
+
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.*
+import android.widget.ViewSwitcher
+import com.tangem.tap.features.wallet.redux.ProgressState
+import com.tangem.wallet.R
+
+/**
+[REDACTED_AUTHOR]
+ */
+class RefreshBalanceWidget(
+ private val root: ViewGroup
+) : ViewStateWidget {
+
+ var isShowing: Boolean? = null
+ private set
+
+ override val mainView: View = root.findViewById(R.id.btn_refresh_balance)
+
+
+ private val viewSwitcher: ViewSwitcher by lazy { mainView.findViewById(R.id.switcher_refresh_arrow_to_progress) }
+
+ private val arrowView = mainView.findViewById(R.id.imv_arrow_refresh)
+
+ private var progressViewAnimation: Animation? = null
+
+ init {
+ viewSwitcher.inAnimation = AlphaAnimation(0f, 1f).apply {
+ duration = 400
+ interpolator = LinearInterpolator()
+ }
+ viewSwitcher.outAnimation = AlphaAnimation(1f, 0f).apply {
+ duration = 400
+ interpolator = LinearInterpolator()
+ }
+ }
+
+ override fun changeState(state: WidgetState) {
+ val progressState = state as? ProgressState ?: return
+
+ val currentStateByView = getState()
+
+ if (progressState == currentStateByView) return
+
+ animateState(progressState)
+ viewSwitcher.showNext()
+
+ }
+
+ private fun animateState(state: ProgressState) {
+ when (state) {
+ ProgressState.Done, ProgressState.Error -> {
+ progressViewAnimation?.cancel()
+ }
+ ProgressState.Loading -> {
+ val animation = RotateAnimation(
+ 0f, 360f,
+ Animation.RELATIVE_TO_SELF, 0.5f,
+ Animation.RELATIVE_TO_SELF, 0.5f
+ )
+ animation.duration = 700
+ animation.interpolator = AccelerateInterpolator()
+ animation.repeatCount = -1
+ arrowView.startAnimation(animation)
+ progressViewAnimation = animation
+ }
+ }
+ }
+
+ fun show(show: Boolean) {
+ if (show == isShowing) return
+
+ isShowing = show
+ if (show) {
+ mainView.startAnimation(ShowAnimation())
+ } else {
+ mainView.startAnimation(AlphaAnimation(1f, 0f).apply { fillAfter = true })
+ }
+ }
+
+ private fun isArrowRefreshActive(): Boolean = viewSwitcher.currentView.id == R.id.fl_arrow_refresh_container
+
+ private fun getState(): ProgressState = if (isArrowRefreshActive()) ProgressState.Done else ProgressState.Loading
+}
+
+class ShowAnimation : AnimationSet(true) {
+ init {
+ addAnimation(ScaleAnimation(
+ 0f, 1f,
+ 0f, 1f,
+ Animation.RELATIVE_TO_SELF, 0.5f,
+ Animation.RELATIVE_TO_SELF, 0.5f)
+ )
+ addAnimation(AlphaAnimation(0f, 1f))
+ duration = 300
+ interpolator = AnticipateOvershootInterpolator()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt
index 246268de7a..48ba0a7291 100644
--- a/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt
+++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt
@@ -1,5 +1,7 @@
package com.tangem.tap.common.toggleWidget
+import android.view.View
+
/**
[REDACTED_AUTHOR]
@@ -7,10 +9,6 @@ package com.tangem.tap.common.toggleWidget
interface WidgetState
interface ViewStateWidget {
+ val mainView: View
fun changeState(state: WidgetState)
-}
-
-sealed class ProgressState : WidgetState {
- object None : ProgressState()
- data class Progress(val progress: Int = 0) : ProgressState()
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt b/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt
new file mode 100644
index 0000000000..32785f0147
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt
@@ -0,0 +1,44 @@
+package com.tangem.tap.common.transitions
+
+import androidx.transition.*
+
+/**
+[REDACTED_AUTHOR]
+ */
+class FrontCardEnterTransition : TransitionSet() {
+ init {
+ ordering = ORDERING_TOGETHER;
+ addTransition(Fade())
+ addTransition(ChangeImageTransform()) // изменения внутри ImageView
+ addTransition(ChangeTransform()) // изменение размеров, углов накона
+ addTransition(ChangeBounds()) // изменение положения
+ }
+}
+
+class FrontCardExitTransition : TransitionSet() {
+ init {
+ ordering = ORDERING_TOGETHER;
+ addTransition(Fade())
+ addTransition(ChangeImageTransform())
+ addTransition(ChangeTransform())
+ addTransition(ChangeBounds())
+ }
+}
+
+class HomeToOnboardingTransition : TransitionSet() {
+ init {
+ ordering = ORDERING_TOGETHER;
+ addTransition(Fade())
+ addTransition(ChangeTransform())
+ addTransition(ChangeBounds())
+ }
+}
+
+class InternalNoteLayoutTransition : TransitionSet() {
+ init {
+ ordering = ORDERING_TOGETHER;
+ addTransition(ChangeTransform())
+ addTransition(ChangeBounds())
+ addTransition(Fade())
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt b/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt
index a6e329ca04..cfd5c2b5c2 100644
--- a/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt
@@ -1,7 +1,7 @@
package com.tangem.tap.domain
import com.tangem.blockchain.common.Blockchain
-import com.tangem.commands.common.network.Result
+import com.tangem.common.services.Result
import com.tangem.tap.network.payid.PayIdVerifyService
import com.tangem.tap.network.payid.VerifyPayIdResponse
import kotlinx.coroutines.Dispatchers
diff --git a/app/src/main/java/com/tangem/tap/domain/ProductType.kt b/app/src/main/java/com/tangem/tap/domain/ProductType.kt
new file mode 100644
index 0000000000..02f0246c82
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/ProductType.kt
@@ -0,0 +1,8 @@
+package com.tangem.tap.domain
+
+/**
+[REDACTED_AUTHOR]
+ */
+enum class ProductType {
+ Note, Wallet, Twin, Other
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt
index af401f69bc..5253a9f91c 100644
--- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt
@@ -1,103 +1,134 @@
package com.tangem.tap.domain
-import androidx.activity.ComponentActivity
-import com.tangem.*
-import com.tangem.commands.CommandResponse
-import com.tangem.commands.PinType
-import com.tangem.commands.SetPinCommand
-import com.tangem.commands.SetPinResponse
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.common.card.CardType
-import com.tangem.commands.wallet.PurgeWalletCommand
-import com.tangem.commands.wallet.PurgeWalletResponse
+import CreateProductWalletAndRescanTask
+import android.content.Context
+import com.tangem.Message
+import com.tangem.TangemSdk
+import com.tangem.common.CardFilter
import com.tangem.common.CompletionResult
-import com.tangem.common.TangemSdkConstants
-import com.tangem.common.extensions.calculateSha256
-import com.tangem.tangem_sdk_new.extensions.init
+import com.tangem.common.SuccessResponse
+import com.tangem.common.card.Card
+import com.tangem.common.card.FirmwareVersion
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.common.core.Config
+import com.tangem.common.core.TangemSdkError
+import com.tangem.operations.CommandResponse
+import com.tangem.operations.pins.CheckUserCodesCommand
+import com.tangem.operations.pins.CheckUserCodesResponse
+import com.tangem.operations.pins.SetUserCodeCommand
+import com.tangem.operations.wallet.PurgeWalletCommand
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.AnalyticsHandler
-import com.tangem.tap.domain.extensions.getDefaultWalletIndex
+import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
-import com.tangem.tap.domain.tasks.ScanNoteResponse
-import com.tangem.tap.domain.tasks.ScanNoteTask
-import com.tangem.tap.domain.twins.isTwinCard
+import com.tangem.tap.domain.tasks.product.ScanProductTask
+import com.tangem.tap.domain.tasks.product.ScanResponse
+import com.tangem.tap.domain.twins.isTangemTwin
import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import java.util.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
-class TangemSdkManager(val activity: ComponentActivity) {
- private val tangemSdk = TangemSdk.init(
- activity, Config(cardFilter = CardFilter(EnumSet.allOf(CardType::class.java)))
- )
+class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) {
- suspend fun scanNote(
- analyticsHandler: AnalyticsHandler, messageRes: Int? = null
- ): CompletionResult {
+ suspend fun scanProduct(
+ analyticsHandler: AnalyticsHandler,
+ messageRes: Int? = null,
+ ): CompletionResult {
analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null)
- return runTaskAsyncReturnOnMain(ScanNoteTask(),
- initialMessage = Message(
- activity.getString(messageRes ?: R.string.initial_message_scan_header)
- ))
+
+ val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header))
+ return runTaskAsyncReturnOnMain(ScanProductTask(), null, message)
+ .also { sendScanFailuresToAnalytics(analyticsHandler, it) }
+ }
+
+ suspend fun createProductWallet(scanResponse: ScanResponse): CompletionResult {
+ return runTaskAsync(
+ CreateProductWalletAndRescanTask(scanResponse.productType),
+ scanResponse.card.cardId,
+ Message(context.getString(R.string.initial_message_create_wallet_body))
+ )
+ }
+
+ private fun sendScanFailuresToAnalytics(
+ analyticsHandler: AnalyticsHandler,
+ result: CompletionResult
+ ) {
+ if (result is CompletionResult.Failure && result.error is TangemSdkError) {
+ (result.error as? TangemSdkError)?.let { error ->
+ analyticsHandler.logCardSdkError(error, FirebaseAnalyticsHandler.ActionToLog.Scan)
+ }
+ }
}
suspend fun createWallet(cardId: String?): CompletionResult {
return runTaskAsyncReturnOnMain(CreateWalletAndRescanTask(), cardId,
- initialMessage = Message(activity.getString(R.string.initial_message_create_wallet_body)))
+ initialMessage = Message(context.getString(R.string.initial_message_create_wallet_body)))
}
- suspend fun eraseWallet(cardId: String?): CompletionResult {
- return runTaskAsyncReturnOnMain(PurgeWalletCommand(
- TangemSdkConstants.getDefaultWalletIndex()),
- cardId,
- initialMessage = Message(activity.getString(R.string.initial_message_purge_wallet_body)))
+ suspend fun eraseWallet(card: Card): CompletionResult {
+ return runTaskAsyncReturnOnMain(
+ PurgeWalletCommand(card.wallets.first().publicKey),
+ card.cardId,
+ initialMessage = Message(context.getString(R.string.initial_message_purge_wallet_body)))
}
- suspend fun setPasscode(cardId: String?): CompletionResult {
- return runTaskAsyncReturnOnMain(SetPinCommand(
- pinType = PinType.Pin2,
- newPin1 = tangemSdk.config.defaultPin1.calculateSha256(),
- newPin2 = null
- ), cardId, initialMessage = Message(activity.getString(R.string.initial_message_change_passcode_body)))
+ suspend fun setPasscode(cardId: String?): CompletionResult {
+ return runTaskAsyncReturnOnMain(
+ SetUserCodeCommand.changePasscode(null),
+ cardId,
+ initialMessage = Message(context.getString(R.string.initial_message_change_passcode_body)))
}
- suspend fun setAccessCode(cardId: String?): CompletionResult {
- return runTaskAsyncReturnOnMain(SetPinCommand(
- pinType = PinType.Pin1,
- newPin1 = null,
- newPin2 = tangemSdk.config.defaultPin2.calculateSha256()
- ), cardId, initialMessage = Message(activity.getString(R.string.initial_message_change_access_code_body)))
+ suspend fun setAccessCode(cardId: String?): CompletionResult {
+ return runTaskAsyncReturnOnMain(
+ SetUserCodeCommand.changeAccessCode(null),
+ cardId,
+ initialMessage = Message(context.getString(R.string.initial_message_change_access_code_body)))
}
- suspend fun setLongTap(cardId: String?): CompletionResult {
- return runTaskAsyncReturnOnMain(SetPinCommand(
- pinType = PinType.Pin1,
- newPin1 = tangemSdk.config.defaultPin1.calculateSha256(),
- newPin2 = tangemSdk.config.defaultPin2.calculateSha256()
- ), cardId, initialMessage = Message(activity.getString(R.string.initial_message_tap_header)))
+ suspend fun setLongTap(cardId: String?): CompletionResult {
+ return runTaskAsyncReturnOnMain(
+ SetUserCodeCommand.resetUserCodes(),
+ cardId,
+ initialMessage = Message(context.getString(R.string.initial_message_tap_header)))
+ }
+
+ suspend fun checkUserCodes(cardId: String?): CompletionResult {
+ return runTaskAsyncReturnOnMain(
+ CheckUserCodesCommand(),
+ cardId,
+ initialMessage = Message(context.getString(R.string.initial_message_tap_header)))
}
suspend fun runTaskAsync(
- runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null,
+ runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null,
): CompletionResult =
- withContext(Dispatchers.IO) {
- suspendCoroutine { continuation ->
- tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result ->
- continuation.resume(result)
- }
+ withContext(Dispatchers.Main) {
+ suspendCoroutine { continuation ->
+ tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result ->
+ continuation.resume(result)
}
}
+ }
private suspend fun runTaskAsyncReturnOnMain(
- runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null,
+ runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null,
): CompletionResult {
val result = runTaskAsync(runnable, cardId, initialMessage)
return withContext(Dispatchers.Main) { result }
}
fun changeDisplayedCardIdNumbersCount(card: Card) {
- tangemSdk.config.cardIdDisplayedNumbersCount = if (card.isTwinCard()) 4 else null
+ tangemSdk.config.cardIdDisplayedNumbersCount = if (card.isTangemTwin()) 4 else null
+ }
+
+ companion object {
+ val config = Config(
+ filter = CardFilter(
+ allowedCardTypes = FirmwareVersion.FirmwareType.values().toList()
+ )
+ )
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt
index 7a3556b33a..978ca5288f 100644
--- a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt
@@ -3,26 +3,25 @@ package com.tangem.tap.domain
import com.tangem.Message
import com.tangem.TangemSdk
import com.tangem.blockchain.common.TransactionSigner
-import com.tangem.commands.SignCommand
-import com.tangem.commands.SignResponse
-import com.tangem.commands.wallet.WalletIndex
import com.tangem.common.CompletionResult
+import com.tangem.tap.domain.tasks.SignHashTask
+import com.tangem.tap.domain.tasks.SignHashesTask
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class TangemSigner(
private val tangemSdk: TangemSdk,
private val initialMessage: Message,
- private val signerCallback: (SignResponse) -> Unit
+ private val signerCallback: (TangemSignerResponse) -> Unit,
) : TransactionSigner {
override suspend fun sign(
hash: ByteArray,
cardId: String,
- walletPublicKey: ByteArray
+ walletPublicKey: ByteArray,
): CompletionResult =
suspendCoroutine { continuation ->
- val command = SignCommand(arrayOf(hash), WalletIndex.PublicKey(walletPublicKey))
+ val command = SignHashTask(hash, walletPublicKey)
tangemSdk.startSessionWithRunnable(
runnable = command,
cardId = cardId,
@@ -30,8 +29,13 @@ class TangemSigner(
) { result ->
when (result) {
is CompletionResult.Success -> {
- signerCallback(result.data)
- continuation.resume(CompletionResult.Success(result.data.signatures.first()))
+ signerCallback(
+ TangemSignerResponse(
+ result.data.totalSignedHashes,
+ result.data.remainingSignatures
+ )
+ )
+ continuation.resume(CompletionResult.Success(result.data.signature))
}
is CompletionResult.Failure ->
continuation.resume(CompletionResult.Failure(result.error))
@@ -43,18 +47,23 @@ class TangemSigner(
override suspend fun sign(
hashes: List,
cardId: String,
- walletPublicKey: ByteArray
+ walletPublicKey: ByteArray,
): CompletionResult> =
suspendCoroutine { continuation ->
- val command = SignCommand(hashes.toTypedArray(), WalletIndex.PublicKey(walletPublicKey))
+ val task = SignHashesTask(hashes, walletPublicKey)
tangemSdk.startSessionWithRunnable(
- runnable = command,
+ runnable = task,
cardId = cardId,
initialMessage = initialMessage,
) { result ->
when (result) {
is CompletionResult.Success -> {
- signerCallback(result.data)
+ signerCallback(
+ TangemSignerResponse(
+ result.data.totalSignedHashes,
+ result.data.remainingSignatures
+ )
+ )
continuation.resume(CompletionResult.Success(result.data.signatures))
}
is CompletionResult.Failure ->
@@ -62,4 +71,9 @@ class TangemSigner(
}
}
}
-}
\ No newline at end of file
+}
+
+data class TangemSignerResponse(
+ val totalSignedHashes: Int?,
+ val remainingSignatures: Int?,
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/TapBlockchainConfig.kt b/app/src/main/java/com/tangem/tap/domain/TapBlockchainConfig.kt
deleted file mode 100644
index b9ec4705a5..0000000000
--- a/app/src/main/java/com/tangem/tap/domain/TapBlockchainConfig.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.tangem.tap.domain
-
-class TapBlockchainConfig {
- companion object {
- val blockchairApiKey: String = "A___0Shpsu4KagE7oSabrw20DfXAqWlT"
- val blockcypherTokens: Set = setOf(
- "aa8184b0e0894b88a5688e01b3dc1e82",
- "56c4ca23c6484c8f8864c32fde4def8d",
- "66a8a37c5e9d4d2c9bb191acfe7f93aa"
- )
- val infuraProjectId: String = "613a0b14833145968b1f656240c7d245"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt
index 521ed462b4..c84e3f0d29 100644
--- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt
@@ -1,7 +1,7 @@
package com.tangem.tap.domain
import androidx.annotation.StringRes
-import com.tangem.TangemError
+import com.tangem.common.core.TangemError
import com.tangem.wallet.R
interface TapErrors
@@ -16,12 +16,13 @@ interface MultiMessageError : TapErrors {
}
sealed class TapError(
- @StringRes val localizedMessage: Int,
- override val args: List? = null
+ @StringRes val messageResource: Int,
+ override val args: List? = null
) : Throwable(), TapErrors, ArgError {
object UnknownError : TapError(R.string.send_error_unknown)
- data class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage))
+ open class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage))
+ object ScanCardError : TapError(R.string.scan_card_error)
object PayIdAlreadyCreated : TapError(R.string.wallet_create_payid_error_already_created)
object PayIdCreatingError : TapError(R.string.wallet_create_payid_error_message)
object PayIdEmptyField : TapError(R.string.wallet_create_payid_empty)
@@ -38,8 +39,13 @@ sealed class TapError(
object DustChange : TapError(R.string.send_error_dust_change)
data class CreateAccountUnderfunded(override val args: List) : TapError(R.string.send_error_no_target_account)
- sealed class XmlError {
- object AssetAccountNotCreated: TapError(R.string.send_error_no_account_xlm)
+ sealed class XmlError {
+ object AssetAccountNotCreated : TapError(R.string.send_error_no_account_xlm)
+ }
+
+ sealed class WalletManagerUpdate {
+ class NoAccountError(amountToCreateAccount: String): CustomError(amountToCreateAccount)
+ class InternalError(message: String): CustomError(message)
}
data class ValidateTransactionErrors(
@@ -60,7 +66,7 @@ fun TapErrors.assembleErrors(): MutableList?>> {
val idList = mutableListOf?>>()
when (this) {
is MultiMessageError -> this.errorList.forEach { idList.addAll(it.assembleErrors()) }
- is TapError -> idList.add(Pair(this.localizedMessage, this.args))
+ is TapError -> idList.add(Pair(this.messageResource, this.args))
}
return idList
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
index a67ec007e0..9133792893 100644
--- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
@@ -1,21 +1,23 @@
package com.tangem.tap.domain
import com.tangem.blockchain.common.*
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.common.card.CardStatus
-import com.tangem.commands.common.network.Result
+import com.tangem.common.card.Card
+import com.tangem.common.services.Result
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
+import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
+import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.TapWorkarounds.isStart2Coin
+import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.domain.configurable.config.ConfigManager
import com.tangem.tap.domain.extensions.*
-import com.tangem.tap.domain.tasks.ScanNoteResponse
+import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.tokens.CardCurrencies
-import com.tangem.tap.domain.twins.TwinsHelper
-import com.tangem.tap.domain.twins.isTwinCard
+import com.tangem.tap.domain.twins.getTwinCardNumber
+import com.tangem.tap.domain.twins.isTangemTwin
import com.tangem.tap.features.tokens.redux.TokensAction
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
@@ -38,28 +40,17 @@ class TapWalletManager {
by lazy { WalletManagerFactory(blockchainSdkConfig) }
suspend fun loadWalletData(walletManager: WalletManager) {
- val result = try {
- walletManager.update()
- Result.Success(walletManager.wallet)
- } catch (exception: Exception) {
- Result.Failure(exception)
- }
- handleUpdateWalletResult(result, walletManager)
+ handleUpdateWalletResult(walletManager.safeUpdate(), walletManager)
}
suspend fun updateWallet(walletManager: WalletManager) {
- val result = try {
- walletManager.update()
- Result.Success(walletManager.wallet)
- } catch (exception: Exception) {
- Result.Failure(exception)
- }
+ val result = walletManager.safeUpdate()
withContext(Dispatchers.Main) {
when (result) {
is Result.Success ->
store.dispatch(WalletAction.UpdateWallet.Success(result.data))
is Result.Failure ->
- store.dispatch(WalletAction.UpdateWallet.Failure(result.error?.localizedMessage))
+ store.dispatch(WalletAction.UpdateWallet.Failure(result.error.localizedMessage))
}
}
@@ -68,7 +59,7 @@ class TapWalletManager {
suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName, wallet: Wallet) {
Timber.d(wallet.getTokens().toString())
val currencies = wallet.getTokens()
- .map { Currency.Token(it, wallet.blockchain) }
+ .map { Currency.Token(it) }
.plus(Currency.Blockchain(wallet.blockchain))
loadFiatRate(fiatCurrency, currencies)
}
@@ -86,9 +77,9 @@ class TapWalletManager {
handleFiatRatesResult(results)
}
- suspend fun onCardScanned(data: ScanNoteResponse, addAnalyticsEvent: Boolean = false) {
+ suspend fun onCardScanned(data: ScanResponse, addAnalyticsEvent: Boolean = false) {
if (addAnalyticsEvent) {
- FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card)
+ FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain)
}
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card)
updateConfigManager(data)
@@ -96,17 +87,15 @@ class TapWalletManager {
withContext(Dispatchers.Main) {
store.dispatch(WalletAction.ResetState)
store.dispatch(GlobalAction.SaveScanNoteResponse(data))
+ store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard))
store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed))
- if (data.card.isTwinCard()) {
- val cardNumber = TwinsHelper.getTwinCardNumber(data.card.cardId)
- if (cardNumber != null) {
- store.dispatch(WalletAction.TwinsAction.SetTwinCard(
- cardNumber, isCreatingTwinCardsAllowed = true
- ))
+ if (data.card.isTangemTwin()) {
+ data.card.getTwinCardNumber()?.let {
+ store.dispatch(WalletAction.TwinsAction.SetTwinCard(it, true))
}
}
- val blockchain = data.card.getBlockchain()
+ val blockchain = data.getBlockchain()
if (blockchain == Blockchain.Ethereum ||
blockchain == Blockchain.EthereumTestnet) {
store.dispatch(TokensAction.LoadCardTokens)
@@ -115,14 +104,14 @@ class TapWalletManager {
}
}
- private fun updateConfigManager(data: ScanNoteResponse) {
+ private fun updateConfigManager(data: ScanResponse) {
val configManager = store.state.globalState.configManager
- val blockchain = data.card.getBlockchain()
+ val blockchain = data.getBlockchain()
if (data.card.isStart2Coin) {
configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled)
configManager?.turnOff(ConfigManager.isTopUpEnabled)
} else if (blockchain == Blockchain.Bitcoin
- || data.card.cardData?.blockchainName == Blockchain.Bitcoin.id) {
+ || data.walletData?.blockchain == Blockchain.Bitcoin.id) {
configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled)
configManager?.resetToDefault(ConfigManager.isTopUpEnabled)
} else {
@@ -131,27 +120,27 @@ class TapWalletManager {
}
}
- suspend fun loadData(data: ScanNoteResponse) {
+ suspend fun loadData(data: ScanResponse) {
withContext(Dispatchers.Main) {
- val artworkId = data.verifyResponse?.artworkInfo?.id
+
+ store.dispatch(WalletAction.LoadCardInfo(data.card))
+
when {
- data.card.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
+ data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain))
- store.dispatch(WalletAction.LoadArtwork(data.card, artworkId))
}
- data.card.getStatus() == CardStatus.Empty ||
- (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> {
+ data.card.wallets.isEmpty() ||
+ (data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> {
store.dispatch(WalletAction.EmptyWallet)
- store.dispatch(WalletAction.LoadArtwork(data.card, artworkId))
}
else -> {
val config = store.state.globalState.configManager?.config ?: return@withContext
- val blockchain = data.card.getBlockchain()
+ val blockchain = data.getBlockchain()
val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data)
- if (blockchain != null && primaryWalletManager != null) {
- val primaryToken = data.card.getToken()
+ if (blockchain != Blockchain.Unknown && primaryWalletManager != null) {
+ val primaryToken = data.getPrimaryToken()
store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain))
if (primaryToken != null) {
@@ -170,31 +159,32 @@ class TapWalletManager {
loadMultiWalletData(data.card, blockchain, null)
}
}
- store.dispatch(WalletAction.SetArtworkId(data.verifyResponse?.artworkInfo?.id))
- store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled))
- store.dispatch(WalletAction.LoadArtwork(data.card, artworkId))
+ val moonPayUserStatus = store.state.globalState.moonPayUserStatus
+ store.dispatch(WalletAction.LoadWallet(
+ allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true,
+ allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true,
+ ))
store.dispatch(WalletAction.LoadFiatRate())
}
}
- store.dispatch(WalletAction.Warnings.CheckIfNeeded)
}
}
private fun loadMultiWalletData(
card: Card, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager?
) {
- val presetTokens = primaryWalletManager?.presetTokens ?: emptySet()
+ val primaryTokens = primaryWalletManager?.cardTokens ?: emptySet()
val savedCurrencies = currenciesRepository.loadCardCurrencies(card.cardId)
if (savedCurrencies == null) {
if (primaryBlockchain != null && primaryWalletManager != null) {
store.dispatch(WalletAction.MultiWallet.SaveCurrencies(
CardCurrencies(
- blockchains = setOf(primaryBlockchain), tokens = presetTokens
+ blockchains = setOf(primaryBlockchain), tokens = primaryTokens
)))
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager))
store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain)))
- store.dispatch(WalletAction.MultiWallet.AddTokens(presetTokens.toList()))
+ store.dispatch(WalletAction.MultiWallet.AddTokens(primaryTokens.toList()))
} else {
val blockchains = setOf(Blockchain.Bitcoin, Blockchain.Ethereum)
store.dispatch(WalletAction.MultiWallet.SaveCurrencies(
@@ -210,7 +200,7 @@ class TapWalletManager {
} else {
val blockchains = savedCurrencies.blockchains.toList()
val walletManagers = if (
- presetTokens.isNotEmpty() &&
+ primaryTokens.isNotEmpty() &&
primaryWalletManager != null && primaryBlockchain != null
) {
val blockchainsWithoutPrimary = blockchains.filterNot { it == primaryBlockchain }
@@ -226,14 +216,14 @@ class TapWalletManager {
}
}
- suspend fun reloadData(data: ScanNoteResponse) {
+ suspend fun reloadData(data: ScanResponse) {
withContext(Dispatchers.Main) {
when {
- data.card.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
+ data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain))
}
- data.card.getStatus() == CardStatus.Empty ||
- (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> {
+ data.card.wallets.isEmpty() ||
+ (data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> {
store.dispatch(WalletAction.EmptyWallet)
}
else -> {
@@ -242,7 +232,11 @@ class TapWalletManager {
return@withContext
}
val config = store.state.globalState.configManager?.config ?: return@withContext
- store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled))
+ val moonpayUserStatus = store.state.globalState.moonPayUserStatus
+ store.dispatch(WalletAction.LoadWallet(
+ allowToBuy = config.isTopUpEnabled && moonpayUserStatus?.isBuyAllowed == true,
+ allowToSell = config.isTopUpEnabled && moonpayUserStatus?.isSellAllowed == true,
+ ))
store.dispatch(WalletAction.LoadFiatRate())
}
}
@@ -254,23 +248,23 @@ class TapWalletManager {
when (result) {
is Result.Success -> store.dispatch(WalletAction.LoadWallet.Success(result.data))
is Result.Failure -> {
- if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) {
- store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection))
- return@withContext
- }
- val error = result.error
- val blockchain = walletManager.wallet.blockchain
- if (error != null && blockchain.isNoAccountError(error)) {
- val token = walletManager.wallet.getFirstToken()
- val amountToCreateAccount = blockchain.amountToCreateAccount(token)
- if (amountToCreateAccount != null) {
- store.dispatch(WalletAction.LoadWallet.NoAccount(
- walletManager.wallet,
- amountToCreateAccount.toString()))
- return@withContext
+ val error = (result.error as? TapError) ?: TapError.UnknownError
+ when (error) {
+ TapError.NoInternetConnection -> {
+ store.dispatch(WalletAction.LoadData.Failure(error))
+ }
+ is TapError.WalletManagerUpdate.NoAccountError -> {
+ store.dispatch(WalletAction.LoadWallet
+ .NoAccount(walletManager.wallet, error.customMessage))
+ }
+ is TapError.WalletManagerUpdate.InternalError -> {
+ store.dispatch(WalletAction.LoadWallet
+ .Failure(walletManager.wallet, error.customMessage))
+ }
+ else -> {
+ store.dispatchDebugErrorNotification(error)
}
}
- store.dispatch(WalletAction.LoadWallet.Failure(walletManager.wallet, result.error?.localizedMessage))
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt
index b64b862627..9003c9f4fc 100644
--- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt
@@ -1,12 +1,13 @@
package com.tangem.tap.domain
import com.tangem.blockchain.common.Blockchain
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.common.card.EllipticCurve
-import com.tangem.commands.common.card.masks.Product
-import com.tangem.tap.domain.TapWorkarounds.isNote
+import com.tangem.common.card.Card
+import com.tangem.common.card.EllipticCurve
+import com.tangem.common.card.FirmwareVersion
import com.tangem.tap.domain.TapWorkarounds.isStart2Coin
+import com.tangem.tap.domain.TapWorkarounds.isTangemNote
import com.tangem.tap.domain.extensions.getSingleWallet
+import com.tangem.tap.domain.twins.isTangemTwin
import java.util.*
object TapWorkarounds {
@@ -16,41 +17,25 @@ object TapWorkarounds {
}
val Card.isStart2Coin: Boolean
- get() = isStart2CoinIssuer(cardData?.issuerName)
+ get() = isStart2CoinIssuer(issuer.name)
+
+ val Card.isTestCard: Boolean
+ get() = batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH)
fun Card.isExcluded(): Boolean {
- val cardData = this.cardData ?: return false
- val productMask = cardData.productMask
- val excludedBatch = excludedBatches.contains(cardData.batchId)
- val excludedIssuerName = excludedIssuers.contains(cardData.issuerName?.toUpperCase(Locale.US))
- val excludedProductMask = (productMask != null && // product mask is on cards v2.30 and later
- !productMask.contains(Product.Note) && !productMask.contains(Product.TwinCard))
- return excludedBatch || excludedIssuerName || excludedProductMask
-
+ val excludedBatch = excludedBatches.contains(batchId)
+ val excludedIssuerName = excludedIssuers.contains(issuer.name.uppercase(Locale.ROOT))
+ return excludedBatch || excludedIssuerName
}
- fun Card.isNote(): Boolean {
- return notesBatches.contains(cardData?.batchId)
- }
+ fun Card.isTangemNote(): Boolean = tangemNoteBatches.contains(batchId)
+ fun Card.isTangemWallet(): Boolean = tangemWalletBatches.contains(batchId)
- fun Card.isMultiCurrencyWallet(): Boolean {
- return multiCurrencyWalletsBatches.contains(cardData?.batchId)
- }
-
- val Card.noteCurrency: Blockchain?
- get() {
- return when (cardData?.batchId) {
- "AB01" -> Blockchain.Bitcoin
- "AB02" -> Blockchain.Ethereum
- "AB03" -> Blockchain.CardanoShelley
- "AB04" -> Blockchain.Dogecoin
- "AB05" -> Blockchain.Binance
- "AB06" -> Blockchain.XRP
- else -> null
- }
- }
+ fun Card.getTangemNoteBlockchain(): Blockchain? = tangemNoteBatches[batchId]
private const val START_2_COIN_ISSUER = "start2coin"
+ private const val TEST_CARD_BATCH = "99FF"
+ private const val TEST_CARD_ID_STARTS_WITH = "FF99"
private val excludedBatches = listOf(
"0027",
@@ -63,23 +48,23 @@ object TapWorkarounds {
"TTM BANK"
)
- private val notesBatches = listOf(
- "AB01",
- "AB02",
- "AB03",
- "AB04",
- "AB05",
- "AB06",
- )
+ private val tangemWalletBatches = listOf("AC01")
- private val multiCurrencyWalletsBatches = listOf("AC01")
+ private val tangemNoteBatches = mapOf(
+ "AB01" to Blockchain.Bitcoin,
+ "AB02" to Blockchain.Ethereum,
+ "AB03" to Blockchain.CardanoShelley,
+ "AB04" to Blockchain.Dogecoin,
+ "AB05" to Blockchain.Binance,
+ "AB06" to Blockchain.XRP,
+ )
}
+val DELAY_SDK_DIALOG_CLOSE = 1400L
+
val Card.isMultiwalletAllowed: Boolean
get() {
- return cardData?.productMask?.contains(Product.TwinCard) != true
- && !isStart2Coin
- && !isNote()
- && (firmwareVersion.major >= 4 ||
+ return !isTangemTwin() && !isStart2Coin && !isTangemNote()
+ && (firmwareVersion >= FirmwareVersion.MultiWalletAvailable ||
getSingleWallet()?.curve == EllipticCurve.Secp256k1)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt b/app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt
deleted file mode 100644
index dfa301ab78..0000000000
--- a/app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.tangem.tap.domain
-
-import android.net.Uri
-import android.util.Base64
-import com.tangem.tap.common.redux.global.CryptoCurrencyName
-import javax.crypto.Mac
-import javax.crypto.spec.SecretKeySpec
-
-class TopUpHelper {
-
- companion object {
-// const val REDIRECT_URL = "https://success.tangem.com"
-
- private const val BASE_URL = "https://buy.moonpay.io"
- private const val API_KEY_PATH = "?apiKey="
- private const val CURRENCY_PATH = "¤cyCode="
- private const val WALLET_ADDRESS_PATH = "&walletAddress="
-// private const val REDIRECT_URL_PATH = "&redirectUrl="
- private const val SIGNATURE_PATH = "&signature="
-
- fun getUrl(cryptoCurrencyName: CryptoCurrencyName, walletAddress: String, apiKey: String, secretKey: String): String {
- val originalQuery = API_KEY_PATH + apiKey.urlEncode() +
- CURRENCY_PATH + cryptoCurrencyName.urlEncode() +
- WALLET_ADDRESS_PATH + walletAddress.urlEncode()
-// REDIRECT_URL_PATH + REDIRECT_URL.urlEncode()
- val signature = createSignature(originalQuery, secretKey)
-
- return BASE_URL + originalQuery + SIGNATURE_PATH + signature.urlEncode()
- }
-
- private fun String.urlEncode(): String {
- return Uri.encode(this)
- }
-
-
- private fun createSignature(data: String, key: String): String {
- val sha256Hmac = Mac.getInstance("HmacSHA256")
- val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256")
- sha256Hmac.init(secretKey)
- val sha256encoded = sha256Hmac.doFinal(data.toByteArray())
- return Base64.encodeToString(sha256encoded, Base64.NO_WRAP)
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt
new file mode 100644
index 0000000000..c06d9b0001
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt
@@ -0,0 +1,49 @@
+package com.tangem.tap.domain
+
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import android.os.Handler
+import android.os.Looper
+import com.squareup.picasso.Picasso
+import com.squareup.picasso.Target
+import com.tangem.common.services.Result
+
+/**
+[REDACTED_AUTHOR]
+ */
+class UrlBitmapLoader {
+ private val mainHandler = Handler(Looper.getMainLooper())
+
+ fun loadBitmap(url: String, callback: (Result) -> Unit) {
+ val target = DownloadTarget(callback)
+ protectedFromGarbageCollectorTargets.add(target)
+ mainHandler.post { Picasso.get().load(url).into(target) }
+ }
+
+ fun loadBitmap(url: String, target: DownloadTarget) {
+ protectedFromGarbageCollectorTargets.add(target)
+ Picasso.get().load(url).into(target)
+ }
+
+}
+
+private val protectedFromGarbageCollectorTargets = mutableListOf()
+
+// It adds the ability to trigger multiple downloads with a unique callback.
+open class DownloadTarget(
+ val callback: (Result) -> Unit,
+) : Target {
+
+ override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) {
+ callback(Result.Success(bitmap))
+ protectedFromGarbageCollectorTargets.remove(this)
+ }
+
+ override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
+ callback.invoke(Result.Failure(e ?: Exception("Unknown exception")))
+ protectedFromGarbageCollectorTargets.remove(this)
+ }
+
+ override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt
index 58096022fd..e08ba924b5 100644
--- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt
@@ -1,25 +1,25 @@
package com.tangem.tap.domain.configurable.config
import com.tangem.blockchain.common.BlockchainSdkConfig
-import com.tangem.tangem_sdk_new.ui.animation.VoidCallback
+import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.domain.configurable.Loader
/**
[REDACTED_AUTHOR]
*/
data class Config(
- val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de",
- val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE",
- val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C",
- val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
- val isSendingToPayIdEnabled: Boolean = true,
- val isTopUpEnabled: Boolean = false,
- val isCreatingTwinCardsAllowed: Boolean = false
+ val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de",
+ val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE",
+ val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C",
+ val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
+ val isSendingToPayIdEnabled: Boolean = true,
+ val isTopUpEnabled: Boolean = false,
+ val isCreatingTwinCardsAllowed: Boolean = false
)
class ConfigManager(
- private val localLoader: Loader,
- private val remoteLoader: Loader
+ private val localLoader: Loader,
+ private val remoteLoader: Loader
) {
var config: Config = Config()
@@ -49,10 +49,11 @@ class ConfigManager(
fun resetToDefault(name: String) {
when (name) {
- isSendingToPayIdEnabled -> config = config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled)
+ isSendingToPayIdEnabled -> config =
+ config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled)
isTopUpEnabled -> config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled)
isCreatingTwinCardsAllowed -> config =
- config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed)
+ config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed)
}
}
@@ -60,38 +61,38 @@ class ConfigManager(
val model = featureModel ?: return
config = config.copy(
- isTopUpEnabled = model.isTopUpEnabled,
- isSendingToPayIdEnabled = model.isSendingToPayIdEnabled,
- isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed
+ isTopUpEnabled = model.isTopUpEnabled,
+ isSendingToPayIdEnabled = model.isSendingToPayIdEnabled,
+ isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed
)
defaultConfig = defaultConfig.copy(
- isTopUpEnabled = model.isTopUpEnabled,
- isSendingToPayIdEnabled = model.isSendingToPayIdEnabled,
- isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed
+ isTopUpEnabled = model.isTopUpEnabled,
+ isSendingToPayIdEnabled = model.isSendingToPayIdEnabled,
+ isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed
)
}
private fun setupKey(configValues: ConfigValueModel?) {
val values = configValues ?: return
config = config.copy(
- coinMarketCapKey = values.coinMarketCapKey,
- moonPayApiKey = values.moonPayApiKey,
- moonPayApiSecretKey = values.moonPayApiSecretKey,
- blockchainSdkConfig = BlockchainSdkConfig(
- blockchairApiKey = values.blockchairApiKey,
- blockcypherTokens = values.blockcypherTokens,
- infuraProjectId = values.infuraProjectId
- )
+ coinMarketCapKey = values.coinMarketCapKey,
+ moonPayApiKey = values.moonPayApiKey,
+ moonPayApiSecretKey = values.moonPayApiSecretKey,
+ blockchainSdkConfig = BlockchainSdkConfig(
+ blockchairAuthorizationToken = values.blockchairAuthorizationToken,
+ blockcypherTokens = values.blockcypherTokens,
+ infuraProjectId = values.infuraProjectId
+ )
)
defaultConfig = defaultConfig.copy(
- coinMarketCapKey = values.coinMarketCapKey,
- moonPayApiKey = values.moonPayApiKey,
- moonPayApiSecretKey = values.moonPayApiSecretKey,
- blockchainSdkConfig = BlockchainSdkConfig(
- blockchairApiKey = values.blockchairApiKey,
- blockcypherTokens = values.blockcypherTokens,
- infuraProjectId = values.infuraProjectId
- )
+ coinMarketCapKey = values.coinMarketCapKey,
+ moonPayApiKey = values.moonPayApiKey,
+ moonPayApiSecretKey = values.moonPayApiSecretKey,
+ blockchainSdkConfig = BlockchainSdkConfig(
+ blockchairAuthorizationToken = values.blockchairAuthorizationToken,
+ blockcypherTokens = values.blockcypherTokens,
+ infuraProjectId = values.infuraProjectId
+ )
)
}
diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt
index 69c5f0a8b0..d0f99ccabc 100644
--- a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt
+++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt
@@ -15,7 +15,7 @@ class ConfigValueModel(
val coinMarketCapKey: String,
val moonPayApiKey: String,
val moonPayApiSecretKey: String,
- val blockchairApiKey: String?,
+ val blockchairAuthorizationToken: String?,
val blockcypherTokens: Set?,
val infuraProjectId: String?,
)
diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt
index 3c3ad5f5ce..500f3844b3 100644
--- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt
+++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt
@@ -1,6 +1,5 @@
package com.tangem.tap.domain.configurable.warningMessage
-import android.view.View
import androidx.annotation.StringRes
import com.squareup.moshi.Json
import com.tangem.blockchain.common.Blockchain
@@ -46,7 +45,9 @@ data class WarningMessage(
@Json(name = "temporary")
Temporary, // можно скрыть (кнопка ОК)
- AppRating
+ AppRating,
+
+ TestCard
}
diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt
index fcc45992dd..3cdf14dfea 100644
--- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt
@@ -1,7 +1,7 @@
package com.tangem.tap.domain.configurable.warningMessage
import com.tangem.blockchain.common.Blockchain
-import com.tangem.tangem_sdk_new.ui.animation.VoidCallback
+import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.extensions.containsAny
import com.tangem.tap.common.extensions.removeBy
import com.tangem.wallet.R
@@ -157,6 +157,18 @@ class WarningMessagesManager(
messageFormatArg = remainingSignatures.toString()
)
+ fun testCardWarning(): WarningMessage = WarningMessage(
+ "",
+ "",
+ type = WarningMessage.Type.TestCard,
+ priority = WarningMessage.Priority.Critical,
+ listOf(WarningMessage.Location.MainScreen),
+ null,
+ R.string.alert_title,
+ R.string.warning_testnet_card_message,
+ WarningMessage.Origin.Local
+ )
+
const val REMAINING_SIGNATURES_WARNING = 10
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt
index a89f4fe24c..d658847574 100644
--- a/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt
+++ b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt
@@ -2,7 +2,6 @@ package com.tangem.tap.domain.extensions
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
-import com.tangem.commands.common.card.EllipticCurve
import java.math.BigDecimal
@@ -17,7 +16,7 @@ fun Blockchain.isNoAccountError(exception: Throwable): Boolean {
fun Blockchain.amountToCreateAccount(token: Token? = null): Double? {
return when (this) {
Blockchain.Stellar -> if (token?.symbol == NODL) 1.5 else 1.toDouble()
- Blockchain.XRP -> 20.toDouble()
+ Blockchain.XRP -> 10.toDouble()
else -> null
}
}
@@ -26,17 +25,4 @@ fun Blockchain.minimalAmount(): BigDecimal {
return 1.toBigDecimal().movePointLeft(decimals())
}
-fun Blockchain.getSupportedCurves(): List? {
- return when (this) {
- Blockchain.Unknown -> null
- Blockchain.Bitcoin, Blockchain.BitcoinTestnet, Blockchain.BitcoinCash, Blockchain.Litecoin,
- Blockchain.Ducatus, Blockchain.Ethereum, Blockchain.EthereumTestnet, Blockchain.RSK,
- Blockchain.Binance, Blockchain.BinanceTestnet -> listOf(EllipticCurve.Secp256k1)
- Blockchain.Tezos, Blockchain.XRP -> listOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519)
- Blockchain.Cardano, Blockchain.CardanoShelley, Blockchain.Stellar ->
- listOf(EllipticCurve.Ed25519)
- else -> listOf(EllipticCurve.Secp256k1)
- }
-}
-
private const val NODL = "NODL"
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt
index dfe0e1e2a7..124ae27387 100644
--- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt
+++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt
@@ -1,49 +1,71 @@
package com.tangem.tap.domain.extensions
-import com.tangem.FirmwareConstraints
-import com.tangem.blockchain.common.Blockchain
-import com.tangem.blockchain.common.Token
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.common.card.CardStatus
-import com.tangem.commands.wallet.CardWallet
-import com.tangem.commands.wallet.WalletStatus
-import com.tangem.common.TangemSdkConstants
-import com.tangem.tap.domain.TapWorkarounds.noteCurrency
-
-fun Card.getToken(): Token? {
- val symbol = cardData?.tokenSymbol ?: return null
- val contractAddress = cardData?.tokenContractAddress ?: return null
- val decimals = cardData?.tokenDecimal ?: return null
- if (symbol.isBlank() || contractAddress.isBlank()) return null
- return Token(symbol, contractAddress, decimals)
-}
-
-fun Card.getBlockchain(): Blockchain? {
- if (noteCurrency != null) return noteCurrency
- val blockchainName: String = cardData?.blockchainName ?: return null
- return Blockchain.fromId(blockchainName)
-}
+import com.tangem.common.card.Card
+import com.tangem.common.card.CardWallet
+import com.tangem.common.extensions.toHexString
+import com.tangem.common.services.Result
+import com.tangem.operations.attestation.CardVerifyAndGetInfo
+import com.tangem.operations.attestation.OnlineCardVerifier
+import com.tangem.tap.domain.twins.TwinCardNumber
+import com.tangem.tap.domain.twins.getTwinCardNumber
+import com.tangem.tap.features.wallet.redux.Artwork
fun Card.getSingleWallet(): CardWallet? {
- return wallet(TangemSdkConstants.getDefaultWalletIndex())
+ return wallets.firstOrNull()
}
-fun Card.getStatus(): CardStatus {
- if (firmwareVersion < FirmwareConstraints.AvailabilityVersions.walletData) return status!!
+fun Card.hasWallets(): Boolean = wallets.isNotEmpty()
- return if (wallets.any { it.status == WalletStatus.Loaded }) {
- CardStatus.Loaded
- } else {
- CardStatus.Empty
- }
-}
+fun Card.hasNoWallets(): Boolean = wallets.isEmpty()
+
+fun Card.hasSingleWallet(): Boolean = wallets.size == 1
fun Card.hasSignedHashes(): Boolean {
- return wallets.any { it.status == WalletStatus.Loaded && it.signedHashes ?: 0 > 0 }
+ return wallets.any { it.totalSignedHashes ?: 0 > 0 }
}
fun Card.signedHashesCount(): Int {
- return wallets.map { it.signedHashes ?: 0 }.sum()
+ return wallets.map { it.totalSignedHashes ?: 0 }.sum()
+}
+
+suspend fun Card.getOrLoadCardArtworkUrl(cardInfo: Result? = null): String {
+ fun ifAnyError(): String {
+ return when {
+ cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
+ cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
+ else -> {
+ when (getTwinCardNumber()) {
+ TwinCardNumber.First -> Artwork.TWIN_CARD_1
+ TwinCardNumber.Second -> Artwork.TWIN_CARD_2
+ else -> Artwork.DEFAULT_IMG_URL
+ }
+ }
+ }
+ }
+
+ val cardInfoResult = cardInfo ?: OnlineCardVerifier().getCardInfo(cardId, cardPublicKey)
+ return when (cardInfoResult) {
+ is Result.Success -> {
+ val artworkId = cardInfoResult.data.artwork?.id
+ if (artworkId == null || artworkId.isEmpty()) {
+ ifAnyError()
+ } else {
+ OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
+ }
+ }
+ is Result.Failure -> ifAnyError()
+ }
+}
+
+fun Card.getArtworkUrl(artworkId: String?): String? {
+ return when {
+ artworkId != null -> {
+ OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
+ }
+ cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
+ cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
+ else -> null
+ }
}
val Card.remainingSignatures: Int?
diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt b/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt
deleted file mode 100644
index c8710e3aeb..0000000000
--- a/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.tangem.tap.domain.extensions
-
-import com.tangem.commands.wallet.WalletIndex
-import com.tangem.common.TangemSdkConstants
-
-fun TangemSdkConstants.Companion.getDefaultWalletIndex(): WalletIndex {
- return WalletIndex.Index(oldCardDefaultWalletIndex)
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt
index 68a0ee5b84..99ed8231c4 100644
--- a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt
+++ b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt
@@ -3,12 +3,13 @@ package com.tangem.tap.domain.extensions
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.WalletManagerFactory
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.common.card.EllipticCurve
-import com.tangem.commands.wallet.CardWallet
+import com.tangem.common.card.Card
+import com.tangem.common.card.CardWallet
+import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.hexToBytes
-import com.tangem.tap.domain.tasks.ScanNoteResponse
-import com.tangem.tap.domain.twins.isTwinCard
+import com.tangem.tap.domain.TapWorkarounds.isTestCard
+import com.tangem.tap.domain.tasks.product.ScanResponse
+import com.tangem.tap.domain.twins.isTangemTwin
fun WalletManagerFactory.makeWalletManagerForApp(
card: Card,
@@ -19,34 +20,52 @@ fun WalletManagerFactory.makeWalletManagerForApp(
val wallet = selectWallet(wallets)
val publicKey = wallet?.publicKey ?: return null
val curveToUse = wallet.curve ?: return null
- return makeWalletManager(card.cardId, publicKey, blockchain, curveToUse)
+ return if (card.isTestCard) {
+ blockchain.getTestnetVersion()?.let {
+ makeWalletManager(card.cardId, publicKey, it, curveToUse)
+ }
+ } else {
+ makeWalletManager(card.cardId, publicKey, blockchain, curveToUse)
+ }
}
private fun selectWallet(wallets: List): CardWallet? {
- return when (wallets.size) {
- 0 -> null
- 1 -> wallets[0]
- else -> wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: wallets[0]
- }
+ return when (wallets.size) {
+ 0 -> null
+ 1 -> wallets[0]
+ else -> wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: wallets[0]
+ }
}
fun WalletManagerFactory.makeWalletManagersForApp(
card: Card, blockchains: List,
): List {
- return blockchains.mapNotNull { blockchain -> makeWalletManagerForApp(card, blockchain) }
+ return if (card.isTestCard) {
+ blockchains.mapNotNull { blockchain ->
+ blockchain.getTestnetVersion()?.let { makeWalletManagerForApp(card, it) }
+ }
+ } else {
+ blockchains.mapNotNull { blockchain -> makeWalletManagerForApp(card, blockchain) }
+ }
}
fun WalletManagerFactory.makePrimaryWalletManager(
- data: ScanNoteResponse,
+ data: ScanResponse,
): WalletManager? {
val card = data.card
- val blockchain = card.getBlockchain()
+ val blockchain = if (card.isTestCard) {
+ data.getBlockchain().getTestnetVersion()
+ } else {
+ data.getBlockchain()
+ }
val supportedCurves = blockchain?.getSupportedCurves() ?: return null
+
val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) }
val wallet = selectWallet(wallets)
val publicKey = wallet?.publicKey ?: return null
- val curveToUse = wallet.curve ?: return null
- return if (card.isTwinCard() && data.secondTwinPublicKey != null) {
+ val curveToUse = wallet.curve
+
+ return if (card.isTangemTwin() && data.secondTwinPublicKey != null) {
makeMultisigWalletManager(
cardId = card.cardId,
walletPublicKey = publicKey, pairPublicKey = data.secondTwinPublicKey.hexToBytes(),
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt
index 19b87612a0..1487f07e37 100644
--- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt
@@ -1,28 +1,28 @@
package com.tangem.tap.domain.tasks
-import com.tangem.CardSession
-import com.tangem.CardSessionRunnable
-import com.tangem.FirmwareConstraints
-import com.tangem.TangemSdkError
-import com.tangem.commands.common.card.Card
import com.tangem.common.CompletionResult
-import com.tangem.tasks.CreateWalletTask
-import com.tangem.tasks.PreflightReadCapable
-import com.tangem.tasks.PreflightReadSettings
-import com.tangem.tasks.PreflightReadTask
+import com.tangem.common.card.Card
+import com.tangem.common.card.FirmwareVersion
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.common.core.TangemSdkError
+import com.tangem.common.extensions.guard
+import com.tangem.operations.PreflightReadMode
+import com.tangem.operations.PreflightReadTask
+import com.tangem.operations.wallet.CreateWalletCommand
-class CreateWalletAndRescanTask : CardSessionRunnable, PreflightReadCapable {
- override val requiresPin2 = false
- override fun preflightReadSettings() = PreflightReadSettings.FullCardRead
+@Deprecated("Use CreateProductWalletAndRescanTask instead")
+class CreateWalletAndRescanTask : CardSessionRunnable {
override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) {
- val firmwareVerion = session.environment.card?.firmwareVersion
- if (firmwareVerion == null) {
+ val card = session.environment.card.guard {
callback(CompletionResult.Failure(TangemSdkError.CardError()))
return
}
- val task = if (firmwareVerion < FirmwareConstraints.AvailabilityVersions.walletData) {
- CreateWalletTask()
+ val firmwareVersion = card.firmwareVersion
+
+ val task = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) {
+ CreateWalletCommand(card.supportedCurves.first())
} else {
CreateWalletsTask()
}
@@ -30,7 +30,7 @@ class CreateWalletAndRescanTask : CardSessionRunnable, PreflightReadCapabl
task.run(session) { result ->
when (result) {
is CompletionResult.Success ->
- PreflightReadTask(PreflightReadSettings.FullCardRead).run(session, callback)
+ PreflightReadTask(PreflightReadMode.FullCardRead).run(session, callback)
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
}
}
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt
index b72cf26ae3..6de34f651a 100644
--- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt
@@ -1,51 +1,44 @@
package com.tangem.tap.domain.tasks
-import com.tangem.CardSession
-import com.tangem.CardSessionRunnable
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.common.card.EllipticCurve
-import com.tangem.commands.wallet.CreateWalletCommand
-import com.tangem.commands.wallet.WalletConfig
import com.tangem.common.CompletionResult
-import com.tangem.tasks.PreflightReadSettings
-import com.tangem.tasks.PreflightReadTask
+import com.tangem.common.card.Card
+import com.tangem.common.card.EllipticCurve
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.operations.PreflightReadMode
+import com.tangem.operations.PreflightReadTask
+import com.tangem.operations.wallet.CreateWalletCommand
-class CreateWalletsTask(wallets: List = emptyList()) : CardSessionRunnable {
- override val requiresPin2 = false
+@Deprecated("Use CreateProductWalletsResponse instead")
+class CreateWalletsTask(curves: List? = null) : CardSessionRunnable {
- val wallets = if (wallets.isEmpty()) {
- listOf(
- WalletConfig(null, null, EllipticCurve.Secp256k1, null),
- WalletConfig(null, null, EllipticCurve.Ed25519, null),
- WalletConfig(null, null, EllipticCurve.Secp256r1, null),
- )
- } else {
- wallets
- }
+ private val curves = curves ?: listOf(
+ EllipticCurve.Secp256k1,
+ EllipticCurve.Ed25519,
+ EllipticCurve.Secp256r1,
+ )
- var index = 0
+ private var index = 0
override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) {
- val walletConfig = wallets[index]
- createWallet(walletConfig, session, callback)
+ val curve = curves[index]
+ createWallet(curve, session, callback)
}
private fun createWallet(
- walletConfig: WalletConfig, session: CardSession,
- callback: (result: CompletionResult) -> Unit
+ curve: EllipticCurve, session: CardSession,
+ callback: (result: CompletionResult) -> Unit
) {
- CreateWalletCommand(
- walletConfig, walletIndexValue = walletConfig.curveId!!.toWalletIndex()
- ).run(session) { result ->
+ CreateWalletCommand(curve).run(session) { result ->
when (result) {
is CompletionResult.Success -> {
- if (index == wallets.lastIndex) {
- PreflightReadTask(PreflightReadSettings.FullCardRead).run(session, callback)
+ if (index == curves.lastIndex) {
+ PreflightReadTask(PreflightReadMode.FullCardRead).run(session, callback)
return@run
}
index += 1
- createWallet(wallets[index], session, callback)
+ createWallet(curves[index], session, callback)
}
is CompletionResult.Failure -> {
callback(CompletionResult.Failure(result.error))
@@ -53,12 +46,4 @@ class CreateWalletsTask(wallets: List = emptyList()) : CardSession
}
}
}
-}
-
-private fun EllipticCurve.toWalletIndex(): Int {
- return when (this) {
- EllipticCurve.Secp256k1 -> 0
- EllipticCurve.Ed25519 -> 1
- EllipticCurve.Secp256r1 -> 2
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt
index 066158592c..43425eab48 100644
--- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt
@@ -3,51 +3,37 @@ package com.tangem.tap.domain.tasks
import com.tangem.*
import com.tangem.blockchain.common.BlockchainSdkConfig
import com.tangem.blockchain.common.WalletManagerFactory
-import com.tangem.commands.CommandResponse
-import com.tangem.commands.ReadIssuerDataCommand
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.common.card.CardStatus
-import com.tangem.commands.common.card.EllipticCurve
-import com.tangem.commands.verification.VerifyCardCommand
-import com.tangem.commands.verification.VerifyCardResponse
-import com.tangem.commands.wallet.WalletConfig
import com.tangem.common.CompletionResult
+import com.tangem.common.card.Card
+import com.tangem.common.card.FirmwareVersion
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.common.core.TangemError
import com.tangem.common.extensions.toHexString
+import com.tangem.operations.ScanTask
+import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
+import com.tangem.tap.domain.ProductType
import com.tangem.tap.domain.TapSdkError
import com.tangem.tap.domain.TapWorkarounds.isExcluded
-import com.tangem.tap.domain.TapWorkarounds.isMultiCurrencyWallet
-import com.tangem.tap.domain.TapWorkarounds.isNote
+import com.tangem.tap.domain.TapWorkarounds.isTangemNote
import com.tangem.tap.domain.extensions.getSingleWallet
-import com.tangem.tap.domain.extensions.getStatus
+import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.twins.TwinCardsManager
-import com.tangem.tap.domain.twins.isTwinCard
+import com.tangem.tap.domain.twins.isTangemTwin
import com.tangem.tap.store
-import com.tangem.tasks.PreflightReadCapable
-import com.tangem.tasks.PreflightReadSettings
-import com.tangem.tasks.ScanTask
-import com.tangem.wallet.R
-data class ScanNoteResponse(
- val card: Card,
- val verifyResponse: VerifyCardResponse? = null,
- val secondTwinPublicKey: String? = null,
-) : CommandResponse
+class ScanNoteTask(val card: Card? = null) : CardSessionRunnable {
-class ScanNoteTask(val card: Card? = null) : CardSessionRunnable, PreflightReadCapable {
- override val requiresPin2 = false
-
- override fun preflightReadSettings() = PreflightReadSettings.FullCardRead
-
- override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) {
+ override fun run(
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit,
+ ) {
ScanTask().run(session) { result ->
when (result) {
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
is CompletionResult.Success -> {
- val card = this.card?.copy(
- isPin1Default = result.data.isPin1Default,
- isPin2Default = result.data.isPin2Default,
- ) ?: result.data
+ val card = this.card ?: result.data
val error = getErrorIfExcludedCard(card)
if (error != null) {
@@ -55,60 +41,42 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit
- ) {
- VerifyCardCommand(true).run(session) { verifyResult ->
- when (verifyResult) {
- is CompletionResult.Success -> {
- if (card.isTwinCard()) {
- dealWithTwinCard(card, session, verifyResult.data, callback)
- } else if (card.firmwareVersion.major >= 4) {
- createMissingWalletsIfNeeded(card, session, verifyResult.data, callback)
+ if (card.isTangemTwin()) {
+ dealWithTwinCard(card, session, callback)
+ } else if (!card.isTangemNote() && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) {
+ createMissingWalletsIfNeeded(card, session, callback)
} else {
- callback(CompletionResult.Success(ScanNoteResponse(card, verifyResult.data)))
+ callback(CompletionResult.Success(
+ ScanResponse(card, ProductType.Other, session.environment.walletData)))
}
}
- is CompletionResult.Failure -> {
- callback(CompletionResult.Failure(TangemSdkError.CardVerificationFailed()))
- }
}
}
}
private fun createMissingWalletsIfNeeded(
- card: Card, session: CardSession, verifyResponse: VerifyCardResponse,
- callback: (result: CompletionResult) -> Unit
+ card: Card, session: CardSession,
+ callback: (result: CompletionResult) -> Unit,
) {
- if (card.getStatus() == CardStatus.Empty) {
- callback(CompletionResult.Success(ScanNoteResponse(card, verifyResponse)))
+ val walletData = session.environment.walletData
+ if (card.wallets.isEmpty()) {
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData)))
return
}
val curvesPresent = card.wallets.map { it.curve }
- val curvesToCreate = EllipticCurve.values().subtract(curvesPresent)
+// val curvesToCreate = EllipticCurve.values().subtract(curvesPresent)
+ val curvesToCreate = card.supportedCurves.subtract(curvesPresent)
if (curvesToCreate.isEmpty()) {
- callback(CompletionResult.Success(ScanNoteResponse(card, verifyResponse)))
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData)))
return
}
- val configs = curvesToCreate.map { curve ->
- WalletConfig(
- isReusable = null, prohibitPurgeWallet = null, curveId = curve,
- signingMethods = null
- )
- }
- CreateWalletsTask(configs).run(session) { result ->
+ CreateWalletsTask(curvesToCreate.toList()).run(session) { result ->
when (result) {
is CompletionResult.Success ->
- callback(CompletionResult.Success(ScanNoteResponse(result.data, verifyResponse)))
+ callback(CompletionResult.Success(ScanResponse(result.data, ProductType.Other, walletData)))
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
}
}
@@ -116,15 +84,15 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit
+ card: Card, session: CardSession,
+ callback: (result: CompletionResult) -> Unit,
) {
ReadIssuerDataCommand().run(session) { readDataResult ->
when (readDataResult) {
is CompletionResult.Success -> {
val publicKey = card.getSingleWallet()?.publicKey
if (publicKey == null) {
- callback(CompletionResult.Success(ScanNoteResponse(card, null)))
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null)))
return@run
}
val verified = TwinCardsManager.verifyTwinPublicKey(
@@ -133,25 +101,28 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable
- callback(CompletionResult.Success(ScanNoteResponse(card, null)))
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null)))
}
}
}
private fun getErrorIfExcludedCard(card: Card): TangemError? {
if (card.isExcluded()) return TapSdkError.CardForDifferentApp
- if (card.status == CardStatus.Purged) return TangemSdkError.WalletIsPurged()
- if (card.status == CardStatus.NotPersonalized) return TangemSdkError.NotPersonalized()
// Disable new cards on the old version of the app // TODO: remove when cards are supported
- if (card.isMultiCurrencyWallet() || card.isNote()) return UpdateAppToUseThisCard()
+ // Disable new multi-currency HD wallet cards on the old version of the app
+// if (card.isMultiCurrencyWallet()) return UpdateAppToUseThisCard()
return null
}
@@ -161,12 +132,4 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable {
+ override fun run(session: CardSession, callback: CompletionCallback) {
+ SignHashCommand(hash, walletPublicKey).run(session) { response ->
+
+ when (response) {
+ is CompletionResult.Success -> {
+ callback(CompletionResult.Success(TangemSignHashResponse(
+ response.data.signature,
+ response.data.totalSignedHashes,
+ session.environment.card?.wallet(walletPublicKey)?.remainingSignatures
+ )))
+ }
+ is CompletionResult.Failure ->
+ callback(CompletionResult.Failure(response.error))
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt
new file mode 100644
index 0000000000..2755d9aa82
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt
@@ -0,0 +1,35 @@
+package com.tangem.tap.domain.tasks
+
+import com.tangem.common.CompletionResult
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.common.core.CompletionCallback
+import com.tangem.operations.CommandResponse
+import com.tangem.operations.sign.SignHashesCommand
+
+class TangemSignHashesResponse(
+ val signatures: List,
+ val totalSignedHashes: Int?,
+ val remainingSignatures: Int?,
+) : CommandResponse
+
+class SignHashesTask(
+ private val hashes: Collection,
+ private val walletPublicKey: ByteArray,
+) : CardSessionRunnable {
+ override fun run(session: CardSession, callback: CompletionCallback) {
+ SignHashesCommand(hashes.toTypedArray(), walletPublicKey).run(session) { response ->
+ when (response) {
+ is CompletionResult.Success -> {
+ callback(CompletionResult.Success(TangemSignHashesResponse(
+ response.data.signatures,
+ response.data.totalSignedHashes,
+ session.environment.card?.wallet(walletPublicKey)?.remainingSignatures
+ )))
+ }
+ is CompletionResult.Failure ->
+ callback(CompletionResult.Failure(response.error))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt
new file mode 100644
index 0000000000..3b3c6ea2ae
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt
@@ -0,0 +1,119 @@
+import com.tangem.common.CompletionResult
+import com.tangem.common.card.Card
+import com.tangem.common.card.EllipticCurve
+import com.tangem.common.card.FirmwareVersion
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.common.core.TangemSdkError
+import com.tangem.common.extensions.guard
+import com.tangem.operations.PreflightReadMode
+import com.tangem.operations.PreflightReadTask
+import com.tangem.operations.wallet.CreateWalletCommand
+import com.tangem.operations.wallet.CreateWalletResponse
+import com.tangem.tap.domain.ProductType
+import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
+import com.tangem.tap.domain.tasks.product.CreateProductWalletsTask
+import com.tangem.tap.domain.tasks.product.ProductCommandProcessor
+import com.tangem.tap.domain.tasks.product.getCurvesForNonCreatedWallets
+
+/**
+[REDACTED_AUTHOR]
+ */
+class CreateProductWalletAndRescanTask(
+ private val type: ProductType,
+) : CardSessionRunnable {
+
+ override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) {
+ val card = session.environment.card.guard {
+ callback(CompletionResult.Failure(TangemSdkError.CardError()))
+ return
+ }
+ CreateProductWalletTask(card, type).run(session) { result ->
+ when (result) {
+ is CompletionResult.Success -> PreflightReadTask(PreflightReadMode.FullCardRead).run(session, callback)
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
+ }
+ }
+ }
+}
+
+private class CreateProductWalletTask(
+ private val card: Card,
+ private val type: ProductType,
+) : CardSessionRunnable {
+
+ override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) {
+ val commandProcessor = when (type) {
+ ProductType.Note -> CreateWalletTangemNote()
+ ProductType.Twin -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet")
+ ProductType.Wallet -> CreateWalletTangemWallet()
+ else -> CreateWalletOtherCards()
+ }
+ commandProcessor.proceed(card, session, callback)
+ }
+}
+
+private class CreateWalletTangemNote : ProductCommandProcessor {
+ override fun proceed(
+ card: Card,
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit
+ ) {
+ if (card.supportedCurves.isEmpty()) {
+ callback(CompletionResult.Failure(TangemSdkError.CardError()))
+ return
+ }
+
+ val curvesSupportedByBlockchain = card.getTangemNoteBlockchain()?.getSupportedCurves()
+ if (curvesSupportedByBlockchain == null || curvesSupportedByBlockchain.isEmpty()) {
+ callback(CompletionResult.Failure(TangemSdkError.CardError()))
+ return
+ }
+
+ val intersectCurves = card.supportedCurves.intersect(curvesSupportedByBlockchain).toList()
+ if (intersectCurves.isEmpty()) {
+ callback(CompletionResult.Failure(TangemSdkError.CardError()))
+ } else {
+ val curve = if (intersectCurves.contains(EllipticCurve.Secp256k1)) {
+ EllipticCurve.Secp256k1
+ } else {
+ intersectCurves[0]
+ }
+ CreateWalletCommand(curve).run(session, callback)
+
+ }
+ }
+}
+
+private class CreateWalletTangemWallet : ProductCommandProcessor {
+ override fun proceed(
+ card: Card,
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit
+ ) {
+ //TODO:
+ }
+}
+
+private class CreateWalletOtherCards : ProductCommandProcessor {
+
+ override fun proceed(
+ card: Card,
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit
+ ) {
+ val firmwareVersion = card.firmwareVersion
+ val task = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) {
+ CreateProductWalletsTask(listOf(card.supportedCurves.first()))
+ } else {
+ CreateProductWalletsTask(card.getCurvesForNonCreatedWallets())
+ }
+
+ task.run(session) { result ->
+ when (result) {
+ is CompletionResult.Success -> callback(CompletionResult.Success(result.data.createWalletResponses[0]))
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletsTask.kt
new file mode 100644
index 0000000000..fc00138a57
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletsTask.kt
@@ -0,0 +1,49 @@
+package com.tangem.tap.domain.tasks.product
+
+import com.tangem.common.CompletionResult
+import com.tangem.common.card.EllipticCurve
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.operations.CommandResponse
+import com.tangem.operations.wallet.CreateWalletCommand
+import com.tangem.operations.wallet.CreateWalletResponse
+
+/**
+[REDACTED_AUTHOR]
+ */
+class CreateProductWalletsResponse(
+ val createWalletResponses: List
+) : CommandResponse
+
+class CreateProductWalletsTask(
+ private val curves: List,
+) : CardSessionRunnable {
+
+ private val createdWalletsResponses = mutableListOf()
+
+ override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) {
+ val curve = curves[createdWalletsResponses.size]
+ createWallet(curve, session, callback)
+ }
+
+ private fun createWallet(
+ curve: EllipticCurve,
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit
+ ) {
+
+ CreateWalletCommand(curve).run(session) { result ->
+ when (result) {
+ is CompletionResult.Success -> {
+ createdWalletsResponses.add(result.data)
+ if (createdWalletsResponses.size == curves.size) {
+ callback(CompletionResult.Success(CreateProductWalletsResponse(createdWalletsResponses)))
+ return@run
+ }
+ createWallet(curves[createdWalletsResponses.size], session, callback)
+ }
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ProductCommandProcessor.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ProductCommandProcessor.kt
new file mode 100644
index 0000000000..c0810ff887
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ProductCommandProcessor.kt
@@ -0,0 +1,16 @@
+package com.tangem.tap.domain.tasks.product
+
+import com.tangem.common.CompletionResult
+import com.tangem.common.card.Card
+import com.tangem.common.core.CardSession
+
+/**
+[REDACTED_AUTHOR]
+ */
+interface ProductCommandProcessor {
+ fun proceed(
+ card: Card,
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit,
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt
new file mode 100644
index 0000000000..cdb63d456b
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt
@@ -0,0 +1,183 @@
+package com.tangem.tap.domain.tasks.product
+
+import com.tangem.*
+import com.tangem.blockchain.common.Blockchain
+import com.tangem.blockchain.common.Token
+import com.tangem.common.CompletionResult
+import com.tangem.common.card.Card
+import com.tangem.common.card.EllipticCurve
+import com.tangem.common.card.FirmwareVersion
+import com.tangem.common.card.WalletData
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.common.core.TangemError
+import com.tangem.common.extensions.toHexString
+import com.tangem.operations.CommandResponse
+import com.tangem.operations.PreflightReadMode
+import com.tangem.operations.PreflightReadTask
+import com.tangem.operations.ScanTask
+import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
+import com.tangem.tap.domain.ProductType
+import com.tangem.tap.domain.TapSdkError
+import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
+import com.tangem.tap.domain.TapWorkarounds.isExcluded
+import com.tangem.tap.domain.TapWorkarounds.isTangemNote
+import com.tangem.tap.domain.TapWorkarounds.isTangemWallet
+import com.tangem.tap.domain.extensions.getSingleWallet
+import com.tangem.tap.domain.twins.TwinCardsManager
+import com.tangem.tap.domain.twins.isTangemTwin
+
+data class ScanResponse(
+ val card: Card,
+ val productType: ProductType,
+ val walletData: WalletData?,
+ val secondTwinPublicKey: String? = null,
+) : CommandResponse {
+
+ fun getBlockchain(): Blockchain {
+ if (productType == ProductType.Note) return card.getTangemNoteBlockchain() ?: return Blockchain.Unknown
+ val blockchainName: String = walletData?.blockchain ?: return Blockchain.Unknown
+ return Blockchain.fromId(blockchainName)
+ }
+
+ fun getPrimaryToken(): Token? {
+ val cardToken = walletData?.token ?: return null
+ return Token(
+ cardToken.name,
+ cardToken.symbol,
+ cardToken.contractAddress,
+ cardToken.decimals,
+ Blockchain.fromId(walletData.blockchain)
+ )
+ }
+}
+
+class ScanProductTask(val card: Card? = null) : CardSessionRunnable {
+
+ override fun run(
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit,
+ ) {
+ ScanTask().run(session) { result ->
+ when (result) {
+ is CompletionResult.Success -> {
+ val card = this.card ?: result.data
+
+ val error = getErrorIfExcludedCard(card)
+ if (error != null) {
+ callback(CompletionResult.Failure(error))
+ return@run
+ }
+
+ val commandProcessor = when {
+ card.isTangemNote() -> ScanNoteProcessor()
+ card.isTangemTwin() -> ScanTwinProcessor()
+ card.isTangemWallet() -> ScanWalletProcessor()
+ else -> ScanOtherCardsProcessor()
+ }
+ commandProcessor.proceed(card, session, callback)
+ }
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
+ }
+ }
+ }
+
+ private fun getErrorIfExcludedCard(card: Card): TangemError? {
+ if (card.isExcluded()) return TapSdkError.CardForDifferentApp
+
+ return null
+ }
+}
+
+private class ScanNoteProcessor : ProductCommandProcessor {
+ override fun proceed(
+ card: Card,
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit
+ ) {
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Note, session.environment.walletData)))
+ }
+}
+
+private class ScanWalletProcessor : ProductCommandProcessor {
+ override fun proceed(
+ card: Card,
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit
+ ) {
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Wallet, session.environment.walletData)))
+ }
+}
+
+private class ScanTwinProcessor : ProductCommandProcessor {
+ override fun proceed(
+ card: Card,
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit,
+ ) {
+ ReadIssuerDataCommand().run(session) { readDataResult ->
+ when (readDataResult) {
+ is CompletionResult.Success -> {
+ val publicKey = card.getSingleWallet()?.publicKey
+ if (publicKey == null) {
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null)))
+ return@run
+ }
+ val verified = TwinCardsManager.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey)
+ if (verified) {
+ val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65)
+ val walletData = session.environment.walletData
+ val response = ScanResponse(card, ProductType.Twin, walletData, twinPublicKey.toHexString())
+ callback(CompletionResult.Success(response))
+ } else {
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null)))
+ }
+ }
+ is CompletionResult.Failure ->
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null)))
+ }
+ }
+ }
+
+}
+
+private class ScanOtherCardsProcessor : ProductCommandProcessor {
+
+ override fun proceed(card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit) {
+ val walletData = session.environment.walletData
+
+ if (card.wallets.isEmpty() || card.firmwareVersion < FirmwareVersion.MultiWalletAvailable) {
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData)))
+ return
+ }
+
+ val curvesToCreate = card.getCurvesForNonCreatedWallets()
+ if (curvesToCreate.isEmpty()) {
+ callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData)))
+ return
+ }
+
+ CreateProductWalletsTask(curvesToCreate).run(session) { result ->
+ when (result) {
+ is CompletionResult.Success -> {
+ PreflightReadTask(PreflightReadMode.FullCardRead, card.cardId).run(session) { readResult ->
+ when (readResult) {
+ is CompletionResult.Success -> {
+ val response = ScanResponse(readResult.data, ProductType.Other, walletData)
+ callback(CompletionResult.Success(response))
+ }
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error))
+ }
+ }
+ }
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
+ }
+ }
+ }
+}
+
+fun Card.getCurvesForNonCreatedWallets(): List {
+ val curvesPresent = wallets.map { it.curve }
+ val curvesForNonCreatedWallets = supportedCurves.subtract(curvesPresent)
+ return curvesForNonCreatedWallets.toList()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt
index c5d9d91396..032c0b97ca 100644
--- a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt
+++ b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt
@@ -3,7 +3,7 @@ package com.tangem.tap.domain.termsOfUse
import android.content.res.Resources
import android.net.Uri
import androidx.core.os.ConfigurationCompat
-import com.tangem.commands.common.card.Card
+import com.tangem.common.card.Card
import java.util.*
/**
@@ -13,8 +13,8 @@ class CardTou {
private val locale: Locale = ConfigurationCompat.getLocales(Resources.getSystem().configuration).get(0)
fun getUrl(card: Card): Uri? {
- val issuerName = card.cardData?.issuerName ?: return null
- if (issuerName.toLowerCase() != "start2coin") return null
+ val issuerName = card.issuer.name ?: return null
+ if (issuerName.lowercase(Locale.getDefault()) != "start2coin") return null
val baseUrl = "https://app.tangem.com/tou/"
val regionCode = regionCode(card.cardId) ?: "fr"
diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt
index 754d7ea239..26aa55369c 100644
--- a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt
@@ -2,22 +2,26 @@ package com.tangem.tap.domain.tokens
import android.app.Application
import android.content.Context
+import com.squareup.moshi.Json
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Types
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
-import com.tangem.commands.common.card.FirmwareVersion
+import com.tangem.common.card.FirmwareVersion
import com.tangem.tap.common.extensions.readJsonFileToString
import com.tangem.tap.network.createMoshi
class CurrenciesRepository(val context: Application) {
private val moshi = createMoshi()
+ private val blockchainsAdapter: JsonAdapter> = moshi.adapter(
+ Types.newParameterizedType(Set::class.java, Blockchain::class.java)
+ )
private val tokensAdapter: JsonAdapter> = moshi.adapter(
Types.newParameterizedType(Set::class.java, TokenDao::class.java)
)
- private val blockchainsAdapter: JsonAdapter> = moshi.adapter(
- Types.newParameterizedType(Set::class.java, Blockchain::class.java)
+ private val obsoleteTokensAdapter: JsonAdapter> = moshi.adapter(
+ Types.newParameterizedType(Set::class.java, ObsoleteTokenDao::class.java)
)
fun loadCardCurrencies(cardId: String): CardCurrencies? {
@@ -57,11 +61,20 @@ class CurrenciesRepository(val context: Application) {
}
private fun loadSavedTokens(cardId: String): Set {
+ val json = try {
+ context.readFileText(getFileNameForTokens(cardId))
+ } catch (exception: Exception) {
+ return emptySet()
+ }
+
return try {
- val json = context.readFileText(getFileNameForTokens(cardId))
tokensAdapter.fromJson(json)!!.map { it.toToken() }.toSet()
} catch (exception: Exception) {
- emptySet()
+ try {
+ obsoleteTokensAdapter.fromJson(json)!!.map { it.toToken() }.toSet()
+ } catch (exception: Exception) {
+ emptySet()
+ }
}
}
@@ -93,79 +106,132 @@ class CurrenciesRepository(val context: Application) {
}
}
- fun getPopularTokens(): List {
- val json = context.assets.readJsonFileToString(POPULAR_TOKENS_FILE_NAME)
- return tokensAdapter.fromJson(json)!!.map { it.toToken() }
+ fun getPopularTokens(isTestNet: Boolean = false): List {
+ val ethereumTokensFileName =
+ if (isTestNet) ETHEREUM_TESTNET_TOKENS_FILE_NAME else ETHEREUM_TOKENS_FILE_NAME
+ val bscTokensFileName =
+ if (isTestNet) BSC_TESTNET_TOKENS_FILE_NAME else BSC_TOKENS_FILE_NAME
+ val binanceTokensFileName =
+ if (isTestNet) BINANCE_TESTNET_TOKENS_FILE_NAME else BINANCE_TOKENS_FILE_NAME
+
+ val ethereumTokensJson = context.assets.readJsonFileToString(ethereumTokensFileName)
+ val bscTokensJson = context.assets.readJsonFileToString(bscTokensFileName)
+ val binanceTokensJson = context.assets.readJsonFileToString(binanceTokensFileName)
+
+ return tokensAdapter.fromJson(ethereumTokensJson)!!.map { it.toToken() } +
+ tokensAdapter.fromJson(bscTokensJson)!!.map { it.toToken() } +
+ tokensAdapter.fromJson(binanceTokensJson)!!.map { it.toToken() }
}
- fun getBlockchains(cardFirmware: FirmwareVersion?): List {
- return if (cardFirmware == null || cardFirmware.major < 4) {
- secp256k1Blockchains
+ fun getBlockchains(
+ cardFirmware: FirmwareVersion?,
+ isTestNet: Boolean = false
+ ): List {
+ return if (cardFirmware == null || cardFirmware < FirmwareVersion.MultiWalletAvailable) {
+ Blockchain.secp256k1Blockchains(isTestNet)
} else {
- secp256k1Blockchains + ed25519Blockchains
+ Blockchain.secp256k1Blockchains(isTestNet) + Blockchain.ed25519OnlyBlockchains(isTestNet)
}
}
companion object {
- private const val POPULAR_TOKENS_FILE_NAME = "erc20_tokens"
+ private const val ETHEREUM_TOKENS_FILE_NAME = "ethereum_tokens"
+ private const val ETHEREUM_TESTNET_TOKENS_FILE_NAME = "ethereum_tokens_testnet"
+ private const val BSC_TOKENS_FILE_NAME = "bsc_tokens"
+ private const val BSC_TESTNET_TOKENS_FILE_NAME = "bsc_tokens_testnet"
+ private const val BINANCE_TOKENS_FILE_NAME = "binance_tokens"
+ private const val BINANCE_TESTNET_TOKENS_FILE_NAME = "binance_tokens_testnet"
private const val FILE_NAME_PREFIX_TOKENS = "tokens"
private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains"
fun getFileNameForTokens(cardId: String): String = "${FILE_NAME_PREFIX_TOKENS}_$cardId"
fun getFileNameForBlockchains(cardId: String): String =
"${FILE_NAME_PREFIX_BLOCKCHAINS}_$cardId"
-
- private val secp256k1Blockchains = listOf(
- Blockchain.Bitcoin,
- Blockchain.BitcoinCash,
- Blockchain.Binance,
- Blockchain.BSC,
- Blockchain.Litecoin,
- Blockchain.XRP,
- Blockchain.Tezos,
- Blockchain.Ethereum,
- Blockchain.RSK,
- Blockchain.Polygon,
- Blockchain.Dogecoin,
- )
- private val ed25519Blockchains = listOf(Blockchain.CardanoShelley, Blockchain.Stellar)
}
}
-
@JsonClass(generateAdapter = true)
data class TokenDao(
val name: String,
val symbol: String,
val contractAddress: String,
val decimalCount: Int,
+ @Json(name = "blockchain")
+ val blockchainDao: BlockchainDao
) {
fun toToken(): Token {
- return Token(name = name,
+ return Token(
+ name = name,
symbol = symbol,
contractAddress = contractAddress,
- decimals = decimalCount)
+ decimals = decimalCount,
+ blockchain = blockchainDao.toBlockchain()
+ )
}
companion object {
fun fromToken(token: Token): TokenDao {
- return TokenDao(name = token.name,
+ return TokenDao(
+ name = token.name,
symbol = token.symbol,
contractAddress = token.contractAddress,
- decimalCount = token.decimals)
-
+ decimalCount = token.decimals,
+ blockchainDao = BlockchainDao.fromBlockchain(token.blockchain)
+ )
}
}
}
+@JsonClass(generateAdapter = true)
+data class BlockchainDao(
+ @Json(name = "key")
+ val name: String,
+ @Json(name = "testnet")
+ val isTestNet: Boolean
+) {
+ fun toBlockchain(): Blockchain {
+ val blockchain = Blockchain.values().find { it.name.lowercase() == name.lowercase() }
+ ?: throw Exception("Invalid BlockchainDao")
+ return if (!isTestNet) blockchain else blockchain.getTestnetVersion()
+ ?: throw Exception("Invalid BlockchainDao")
+ }
+
+ companion object {
+ fun fromBlockchain(blockchain: Blockchain): BlockchainDao {
+ val name = blockchain.name.removeSuffix("Testnet").lowercase()
+ val isTestnet = blockchain.name.endsWith("Testnet")
+ return BlockchainDao(name, isTestnet)
+ }
+ }
+}
+
+@JsonClass(generateAdapter = true)
+data class ObsoleteTokenDao(
+ val name: String,
+ val symbol: String,
+ val contractAddress: String,
+ val decimalCount: Int,
+) {
+ fun toToken(): Token {
+ return Token(
+ name = name,
+ symbol = symbol,
+ contractAddress = contractAddress,
+ decimals = decimalCount
+ )
+ }
+}
+
@JsonClass(generateAdapter = true)
data class CardCurrenciesDao(
val tokens: Set,
val blockchains: Set,
) {
fun toCardCurrencies(): CardCurrencies {
- return CardCurrencies(tokens = tokens.map { it.toToken() }.toSet(),
- blockchains = blockchains)
+ return CardCurrencies(
+ tokens = tokens.map { it.toToken() }.toSet(),
+ blockchains = blockchains
+ )
}
companion object {
diff --git a/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt b/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt
new file mode 100644
index 0000000000..775d3748a7
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt
@@ -0,0 +1,45 @@
+package com.tangem.tap.domain.topup
+
+import com.tangem.Message
+import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
+import com.tangem.blockchain.common.Amount
+import com.tangem.blockchain.common.AmountType
+import com.tangem.blockchain.common.Token
+import com.tangem.blockchain.extensions.Result
+import com.tangem.tap.common.redux.global.GlobalAction
+import com.tangem.tap.domain.TangemSigner
+import com.tangem.tap.store
+import com.tangem.tap.tangemSdk
+import java.math.BigDecimal
+
+class TopUpManager {
+ suspend fun topUpTestErc20Tokens(walletManager: EthereumWalletManager, token: Token) {
+ walletManager.update()
+
+ val amountToSend = Amount(walletManager.wallet.blockchain)
+ val destinationAddress = token.contractAddress
+
+ val feeResult = walletManager.getFee(amountToSend,
+ destinationAddress) as? Result.Success ?: return
+ val fee = feeResult.data[0]
+
+ if ((walletManager.wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO) < fee.value) {
+ return
+ }
+
+ val transaction = walletManager.createTransaction(amountToSend, fee, destinationAddress)
+
+ val signer = TangemSigner(
+ tangemSdk = tangemSdk, Message()
+ ) { signResponse ->
+ store.dispatch(
+ GlobalAction.UpdateWalletSignedHashes(
+ walletSignedHashes = signResponse.totalSignedHashes,
+ walletPublicKey = walletManager.wallet.publicKey,
+ remainingSignatures = signResponse.remainingSignatures
+ )
+ )
+ }
+ walletManager.send(transaction, signer)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt b/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt
new file mode 100644
index 0000000000..f16a2591b2
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt
@@ -0,0 +1,102 @@
+package com.tangem.tap.domain.topup
+
+import android.net.Uri
+import android.util.Base64
+import com.tangem.blockchain.common.Blockchain
+import com.tangem.tap.common.redux.global.CryptoCurrencyName
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
+
+class TradeCryptoHelper {
+
+ companion object {
+
+ const val TRANSACTION_ID_PARAM = "transactionId"
+ const val CURRENCY_CODE_PARAM = "baseCurrencyCode"
+ const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount"
+ const val DEPOSIT_WALLET_ADDRESS_PARAM = "depositWalletAddress"
+
+ private const val REDIRECT_URL_BUY = "tangem://success.tangem.com"
+ private const val REDIRECT_URL_SELL_REQUEST = "tangem://sell-request.tangem.com"
+
+ private const val BASE_URL_BUY =
+ "https://buy.moonpay.io"
+// "https://buy-staging.moonpay.io" // TESTNET
+ private const val BASE_URL_SELL =
+ "https://sell.moonpay.com"
+// "https://sell-staging.moonpay.com" // TESTNET
+
+ private const val API_KEY_PATH = "?apiKey="
+ private const val CURRENCY_PATH = "¤cyCode="
+ private const val WALLET_ADDRESS_PATH = "&walletAddress="
+
+ private const val REFUND_WALLET_ADDRESS_PATH = "&refundWalletAddress="
+ private const val BASE_CURRENCY_PATH = "&baseCurrencyCode="
+
+ private const val REDIRECT_URL_PATH = "&redirectURL="
+ private const val SIGNATURE_PATH = "&signature="
+
+ private const val TRANSACTION_RECEIPT_PATH = "transaction_receipt?transactionId="
+
+ val AVAILABLE_TO_BUY: Set = setOf(
+ "ZRX", "AAVE", "ALGO", "AXS", "BAT", "BNB", "BUSD", "BTC", "BCH", "BTT", "ADA", "CELO", "CUSD", "LINK", "CHZ", "COMP", "ATOM", "DAI", "DASH", "MANA", "DGB", "DOGE", "EGLD",
+ "ENJ", "EOS", "ETC", "ETH", "KETH", "RINKETH", "FIL", "HBAR", "MIOTA", "KAVA", "KLAY", "LBC", "LTC", "LUNA", "MKR", "OM", "MATIC", "NANO", "NEAR", "XEM", "NEO", "NIM", "OKB",
+ "OMG", "ONG", "ONT", "DOT", "QTUM", "RVN", "RFUEL", "KEY", "SRM", "SOL", "XLM", "STMX", "SNX", "KRT", "UST", "USDT", "XTZ", "RUNE", "SAND", "TOMO", "AVA", "TRX", "TUSD", "UNI",
+ "USDC", "UTK", "VET", "WAXP", "WBTC", "XRP", "ZEC", "ZIL"
+ )
+
+ val AVAILABLE_TO_SELL: Set = setOf("BTC", "ETH", "BCH")
+
+ fun getUrl(
+ action: Action,
+ blockchain: Blockchain?,
+ cryptoCurrencyName: CryptoCurrencyName,
+ walletAddress: String,
+ apiKey: String,
+ secretKey: String,
+ ): String {
+ val originalQuery: String
+ val baseUrl: String
+ when (action) {
+ Action.Buy -> {
+ if (blockchain?.isTestnet() == true) {
+ return blockchain.getTestnetTopUpUrl() ?: ""
+ }
+ baseUrl = BASE_URL_BUY
+ originalQuery = API_KEY_PATH + apiKey.urlEncode() +
+ CURRENCY_PATH + cryptoCurrencyName.urlEncode() +
+ WALLET_ADDRESS_PATH + walletAddress.urlEncode() +
+ REDIRECT_URL_PATH + REDIRECT_URL_BUY.urlEncode()
+ }
+ Action.Sell -> {
+ baseUrl = BASE_URL_SELL
+ originalQuery = API_KEY_PATH + apiKey.urlEncode() +
+ BASE_CURRENCY_PATH + cryptoCurrencyName.urlEncode() +
+ REFUND_WALLET_ADDRESS_PATH + walletAddress.urlEncode() +
+ REDIRECT_URL_PATH + REDIRECT_URL_SELL_REQUEST.urlEncode()
+ }
+ }
+ val signature = createSignature(originalQuery, secretKey)
+ return baseUrl + originalQuery + SIGNATURE_PATH + signature.urlEncode()
+
+ }
+
+ fun getSellCryptoReceiptUrl(transactionId: String): String {
+ return BASE_URL_SELL + TRANSACTION_RECEIPT_PATH + transactionId
+ }
+
+ private fun String.urlEncode(): String {
+ return Uri.encode(this)
+ }
+
+ private fun createSignature(data: String, key: String): String {
+ val sha256Hmac = Mac.getInstance("HmacSHA256")
+ val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256")
+ sha256Hmac.init(secretKey)
+ val sha256encoded = sha256Hmac.doFinal(data.toByteArray())
+ return Base64.encodeToString(sha256encoded, Base64.NO_WRAP)
+ }
+ }
+
+ enum class Action { Buy, Sell }
+}
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt
index 0251173136..400ee19ae3 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt
@@ -1,34 +1,34 @@
package com.tangem.tap.domain.twins
-import com.tangem.CardSession
-import com.tangem.CardSessionRunnable
-import com.tangem.commands.wallet.CreateWalletResponse
-import com.tangem.commands.wallet.PurgeWalletCommand
import com.tangem.common.CompletionResult
-import com.tangem.common.TangemSdkConstants
-import com.tangem.tap.domain.extensions.getDefaultWalletIndex
+import com.tangem.common.card.EllipticCurve
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.operations.wallet.CreateWalletCommand
+import com.tangem.operations.wallet.CreateWalletResponse
+import com.tangem.operations.wallet.PurgeWalletCommand
import com.tangem.tap.domain.extensions.getSingleWallet
-import com.tangem.tasks.CreateWalletTask
class CreateFirstTwinWalletTask : CardSessionRunnable {
- override val requiresPin2 = false
-
override fun run(
session: CardSession,
callback: (result: CompletionResult) -> Unit,
) {
- if (session.environment.card?.getSingleWallet()?.publicKey != null) {
- PurgeWalletCommand(TangemSdkConstants.getDefaultWalletIndex()).run(session) { response ->
+ val publicKey = session.environment.card?.getSingleWallet()?.publicKey
+ if (publicKey != null) {
+ PurgeWalletCommand(publicKey).run(session) { response ->
when (response) {
is CompletionResult.Success -> {
- session.environment.card = session.environment.card?.changeStatusToEmpty()
- CreateWalletTask().run(session) { callback(it) }
+ session.environment.card = session.environment.card?.setWallets(emptyList())
+ CreateWalletCommand(EllipticCurve.Secp256k1)
+ .run(session) { callback(it) }
}
is CompletionResult.Failure -> callback(CompletionResult.Failure(response.error))
}
}
} else {
- CreateWalletTask().run(session) { callback(it) }
+ CreateWalletCommand(EllipticCurve.Secp256k1)
+ .run(session) { callback(it) }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt
index 8224293d18..966fe4f1d1 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt
@@ -1,14 +1,17 @@
package com.tangem.tap.domain.twins
-import com.tangem.*
-import com.tangem.commands.wallet.CreateWalletResponse
-import com.tangem.commands.wallet.PurgeWalletCommand
+import com.tangem.Message
import com.tangem.common.CompletionResult
-import com.tangem.common.TangemSdkConstants
+import com.tangem.common.KeyPair
+import com.tangem.common.card.EllipticCurve
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
-import com.tangem.tap.domain.extensions.getDefaultWalletIndex
+import com.tangem.operations.wallet.CreateWalletCommand
+import com.tangem.operations.wallet.CreateWalletResponse
+import com.tangem.operations.wallet.PurgeWalletCommand
import com.tangem.tap.domain.extensions.getSingleWallet
-import com.tangem.tasks.CreateWalletTask
class CreateSecondTwinWalletTask(
private val firstPublicKey: String,
@@ -17,25 +20,20 @@ class CreateSecondTwinWalletTask(
private val preparingMessage: Message,
private val creatingWalletMessage: Message
) : CardSessionRunnable {
- override val requiresPin2 = true
-
-
-
override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) {
val card = session.environment.card
-
- if (card?.getSingleWallet()?.publicKey != null) {
-
+ val publicKey = card?.getSingleWallet()?.publicKey
+ if (publicKey != null) {
if (!card.cardId.startsWith(TwinsHelper.getPairCardSeries(firstCardId) ?: "")) {
callback(CompletionResult.Failure(TangemSdkError.WrongCardType()))
return
}
session.setInitialMessage(preparingMessage)
- PurgeWalletCommand(TangemSdkConstants.getDefaultWalletIndex()).run(session) { response ->
+ PurgeWalletCommand(publicKey).run(session) { response ->
when (response) {
is CompletionResult.Success -> {
- session.environment.card = session.environment.card?.changeStatusToEmpty()
+ session.environment.card = session.environment.card?.setWallets(emptyList())
finishTask(session, callback)
}
is CompletionResult.Failure -> callback(CompletionResult.Failure(response.error))
@@ -48,10 +46,10 @@ class CreateSecondTwinWalletTask(
private fun finishTask(session: CardSession, callback: (result: CompletionResult) -> Unit) {
session.setInitialMessage(creatingWalletMessage)
- CreateWalletTask().run(session) { result ->
+ CreateWalletCommand(EllipticCurve.Secp256k1).run(session) { result ->
when (result) {
is CompletionResult.Success -> {
- session.environment.card = session.environment.card?.changeStatusToLoaded()
+ session.environment.card = session.environment.card?.updateWallet(result.data.wallet)
WriteProtectedIssuerDataTask(
firstPublicKey.hexToBytes(), issuerKeys
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt
index 8eb0315241..c035973ac7 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt
@@ -1,30 +1,29 @@
package com.tangem.tap.domain.twins
-import com.tangem.CardSession
-import com.tangem.CardSessionRunnable
-import com.tangem.KeyPair
import com.tangem.common.CompletionResult
-import com.tangem.tap.domain.tasks.ScanNoteResponse
-import com.tangem.tap.domain.tasks.ScanNoteTask
-import com.tangem.tasks.PreflightReadSettings
-import com.tangem.tasks.PreflightReadTask
+import com.tangem.common.KeyPair
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.operations.PreflightReadMode
+import com.tangem.operations.PreflightReadTask
+import com.tangem.tap.domain.tasks.product.ScanProductTask
+import com.tangem.tap.domain.tasks.product.ScanResponse
class FinalizeTwinTask(
private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair,
-) : CardSessionRunnable {
- override val requiresPin2 = true
+) : CardSessionRunnable {
override fun run(
session: CardSession,
- callback: (result: CompletionResult) -> Unit,
+ callback: (result: CompletionResult) -> Unit,
) {
WriteProtectedIssuerDataTask(twinPublicKey, issuerKeys).run(session) { result ->
when (result) {
is CompletionResult.Success ->
- PreflightReadTask(PreflightReadSettings.FullCardRead).run(session) { readResult ->
+ PreflightReadTask(PreflightReadMode.FullCardRead).run(session) { readResult ->
when (readResult) {
is CompletionResult.Success ->
- ScanNoteTask(readResult.data).run(session, callback)
+ ScanProductTask(readResult.data).run(session, callback)
is CompletionResult.Failure ->
callback(CompletionResult.Failure(readResult.error))
}
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt
index ac8e5da05f..faa839cbe6 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt
@@ -3,49 +3,60 @@ package com.tangem.tap.domain.twins
import android.content.Context
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Types
-import com.tangem.KeyPair
import com.tangem.Message
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.common.CompletionResult
+import com.tangem.common.KeyPair
+import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.crypto.CryptoUtils
+import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.readAssetAsString
-import com.tangem.tap.domain.tasks.ScanNoteResponse
+import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.network.createMoshi
import com.tangem.tap.tangemSdkManager
-class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: Context) {
+class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) {
- private val currentCardId: String = scanNoteResponse.card.cardId
+ private val currentCardId: String = scanResponse.card.cardId
private var currentCardPublicKey: String? = null
private var secondCardPublicKey: String? = null
private val issuerKeyPair: KeyPair = getIssuerKeys(
- context, scanNoteResponse.card.issuerPublicKey!!.toHexString()
+ context, scanResponse.card.issuer.publicKey.toHexString()
)
suspend fun createFirstWallet(message: Message): SimpleResult {
val response = tangemSdkManager.runTaskAsync(
- CreateFirstTwinWalletTask(), currentCardId, message
+ CreateFirstTwinWalletTask(), currentCardId, message
)
when (response) {
is CompletionResult.Success -> {
- currentCardPublicKey = response.data.walletPublicKey.toHexString()
+ currentCardPublicKey = response.data.wallet.publicKey.toHexString()
return SimpleResult.Success
}
- is CompletionResult.Failure -> return SimpleResult.failure(response.error)
+ is CompletionResult.Failure -> {
+ (response.error as? TangemSdkError)?.let { error ->
+ FirebaseAnalyticsHandler.logCardSdkError(
+ error,
+ FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
+ card = scanResponse.card
+ )
+ }
+ return SimpleResult.failure(response.error)
+ }
}
}
suspend fun createSecondWallet(
- initialMessage: Message,
- preparingMessage: Message,
- creatingWalletMessage: Message
+ initialMessage: Message,
+ preparingMessage: Message,
+ creatingWalletMessage: Message,
): SimpleResult {
val task = CreateSecondTwinWalletTask(
firstPublicKey = currentCardPublicKey!!,
@@ -54,37 +65,53 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context:
preparingMessage = preparingMessage,
creatingWalletMessage = creatingWalletMessage
)
- val response = tangemSdkManager.runTaskAsync(
- task, null, initialMessage
- )
+ val response = tangemSdkManager.runTaskAsync(task, null, initialMessage)
when (response) {
is CompletionResult.Success -> {
- secondCardPublicKey = response.data.walletPublicKey.toHexString()
+ secondCardPublicKey = response.data.wallet.publicKey.toHexString()
return SimpleResult.Success
}
- is CompletionResult.Failure -> return SimpleResult.failure(response.error)
+ is CompletionResult.Failure -> {
+ (response.error as? TangemSdkError)?.let { error ->
+ FirebaseAnalyticsHandler.logCardSdkError(
+ error,
+ FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
+ card = scanResponse.card
+ )
+ }
+ return SimpleResult.failure(response.error)
+ }
}
}
- suspend fun complete(message: Message): Result {
+ suspend fun complete(message: Message): Result {
val response = tangemSdkManager.runTaskAsync(
- FinalizeTwinTask(secondCardPublicKey!!.hexToBytes(), issuerKeyPair),
- currentCardId, message
+ FinalizeTwinTask(secondCardPublicKey!!.hexToBytes(), issuerKeyPair),
+ currentCardId, message
)
return when (response) {
is CompletionResult.Success -> Result.Success(response.data)
- is CompletionResult.Failure -> Result.failure(response.error)
+ is CompletionResult.Failure -> {
+ (response.error as? TangemSdkError)?.let { error ->
+ FirebaseAnalyticsHandler.logCardSdkError(
+ error,
+ FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData,
+ card = scanResponse.card
+ )
+ }
+ Result.failure(response.error)
+ }
}
}
companion object {
fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean {
- if (issuerData.size < 65) return false
+ if (issuerData.size < 65 || cardWalletPublicKey == null) return false
+
val publicKey = issuerData.sliceArray(0 until 65)
val signedKey = issuerData.sliceArray(65 until issuerData.size)
- return (cardWalletPublicKey != null &&
- CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey))
+ return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey)
}
private fun getIssuerKeys(context: Context, publicKey: String): KeyPair {
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt
new file mode 100644
index 0000000000..63f891eb4d
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt
@@ -0,0 +1,152 @@
+package com.tangem.tap.domain.twins
+
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.animation.PropertyValuesHolder
+import android.view.View
+import androidx.core.animation.doOnEnd
+import com.tangem.tap.common.leapfrogWidget.LeapView
+import com.tangem.tap.common.leapfrogWidget.LeapViewState
+import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget
+
+/**
+[REDACTED_AUTHOR]
+ */
+class TwinsCardWidget(
+ val leapfrogWidget: LeapfrogWidget,
+ val getTopOfAnchorViewForActivateState: () -> Float
+) {
+
+ init {
+ if (leapfrogWidget.getViewsCount() != 2) throw UnsupportedOperationException()
+ }
+
+ fun toWelcome(animate: Boolean = true, onEnd: () -> Unit = {}) {
+ val animator = createAnimator(animate, onEnd)
+ animator.playTogether(
+ createAnimator(TwinCardNumber.First, createWelcomeProperties(TwinCardNumber.First)),
+ createAnimator(TwinCardNumber.Second, createWelcomeProperties(TwinCardNumber.Second))
+ )
+ leapfrogWidget.fold { animator.start() }
+ }
+
+ fun toLeapfrog(animate: Boolean = true, onEnd: () -> Unit = {}) {
+ val animator = createAnimator(animate, onEnd)
+ animator.playTogether(
+ createAnimator(TwinCardNumber.First, createLeapfrogProperties(TwinCardNumber.First)),
+ createAnimator(TwinCardNumber.Second, createLeapfrogProperties(TwinCardNumber.Second))
+ )
+ leapfrogWidget.fold {
+ animator.doOnEnd {
+ leapfrogWidget.initViews()
+ leapfrogWidget.unfold()
+ }
+ animator.start()
+ }
+ }
+
+ fun toActivate(animate: Boolean = true, onEnd: () -> Unit = {}) {
+ val animator = createAnimator(animate, onEnd)
+ animator.playTogether(
+ createAnimator(TwinCardNumber.First, createActivateProperties(TwinCardNumber.First)),
+ createAnimator(TwinCardNumber.Second, createActivateProperties(TwinCardNumber.Second))
+ )
+ animator.start()
+ }
+
+ private fun createAnimator(animate: Boolean, onEnd: () -> Unit): AnimatorSet {
+ return AnimatorSet().apply {
+ duration = if (animate) 400 else 0
+ doOnEnd { onEnd() }
+ }
+ }
+
+ private fun createAnimator(cardNumber: TwinCardNumber, properties: TwinsCardProperties): ObjectAnimator {
+ val view = getLeapViewByCardNumber(cardNumber).view
+ val animator = ObjectAnimator.ofPropertyValuesHolder(view, *properties.createValuesHolders().toTypedArray())
+ view.elevation = properties.elevation
+ return animator
+ }
+
+ private fun createWelcomeProperties(cardNumber: TwinCardNumber): TwinsCardProperties {
+ return when (cardNumber) {
+ TwinCardNumber.First -> {
+ TwinsCardProperties(
+ xTranslation = -180f,
+ yTranslation = -350f,
+ rotation = -3f,
+ elevation = 1f,
+ scale = 1.1f,
+ )
+ }
+ TwinCardNumber.Second -> {
+ TwinsCardProperties(
+ xTranslation = 180f,
+ yTranslation = 10f,
+ rotation = -3f,
+ elevation = 0f,
+ scale = 1.1f,
+ )
+ }
+ }
+ }
+
+ private fun createLeapfrogProperties(cardNumber: TwinCardNumber): TwinsCardProperties {
+ val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state)
+ return twinProperties.copy(
+ xTranslation = 0f,
+ yTranslation = 0f,
+ rotation = 0f,
+ scale = 1f,
+ )
+ }
+
+ private fun createActivateProperties(cardNumber: TwinCardNumber): TwinsCardProperties {
+ val topOfAnchorView = getTopOfAnchorViewForActivateState()
+ val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state)
+ return twinProperties.copy(
+ xTranslation = twinProperties.xTranslation,
+ yTranslation = twinProperties.yTranslation - topOfAnchorView,
+ rotation = 0f,
+ scale = twinProperties.scale - 0.4f,
+ )
+ }
+
+ private fun getLeapViewByCardNumber(cardNumber: TwinCardNumber): LeapView {
+ return when (cardNumber) {
+ TwinCardNumber.First -> leapfrogWidget.getViewByPosition(0)
+ TwinCardNumber.Second -> leapfrogWidget.getViewByPosition(1)
+ }
+ }
+}
+
+private data class TwinsCardProperties(
+ val xTranslation: Float = 0f,
+ val yTranslation: Float = 0f,
+ val rotation: Float = 0f,
+ val elevation: Float = 0f,
+ val scale: Float = 1.1f,
+) {
+
+ fun createValuesHolders(): List {
+ return listOf(
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_X, xTranslation),
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, yTranslation),
+ PropertyValuesHolder.ofFloat(View.ROTATION, rotation),
+ PropertyValuesHolder.ofFloat(View.SCALE_X, scale),
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, scale),
+ )
+ }
+
+ companion object {
+ fun from(leapViewState: LeapViewState): TwinsCardProperties {
+ val leapViewProperties = leapViewState.properties
+
+ return TwinsCardProperties(
+ yTranslation = leapViewProperties.yTranslation,
+ elevation = leapViewProperties.elevationEnd,
+ scale = leapViewProperties.scale,
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt
index 68a70fe179..ebcbb5ff2b 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt
@@ -1,10 +1,6 @@
package com.tangem.tap.domain.twins
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.common.card.CardStatus
-import com.tangem.commands.common.card.masks.Product
-import com.tangem.commands.wallet.WalletStatus
-import com.tangem.tap.common.extensions.isEven
+import com.tangem.common.card.Card
class TwinsHelper {
companion object {
@@ -37,22 +33,6 @@ class TwinsHelper {
}
}
- fun getTwinsCardId(cardId: String): String? {
- val cardIdWithNewSeries = when (getTwinCardNumber(cardId) ?: return null) {
- TwinCardNumber.First -> {
- val index = if (cardId.startsWith(firstCardSeries[0])) 0 else 1
- cardId.replace(firstCardSeries[index], secondCardSeries[index])
- }
- TwinCardNumber.Second -> {
- val index = if (cardId.startsWith(secondCardSeries[0])) 0 else 1
- cardId.replace(secondCardSeries[index], firstCardSeries[index])
- }
- }
- val cardIdWithoutChecksum = cardIdWithNewSeries.dropLast(1)
- val checkSum = cardIdWithoutChecksum.calculateLuhn()
- return cardIdWithoutChecksum + checkSum
- }
-
fun getTwinCardIdForUser(cardId: String): String {
if (cardId.length < 16) return cardId
@@ -66,21 +46,6 @@ class TwinsHelper {
}
}
-private fun String.calculateLuhn(): Int {
- val checksum = this.reversed()
- .mapIndexed { index, c ->
- val digit = if (c in '0'..'9') c - '0' else c - 'A'
- if (!index.isEven()) {
- digit
- } else {
- val newDigit = digit * 2
- if (newDigit >= 10) newDigit - 9 else newDigit
- }
- }.sum()
- .rem(10)
- return (10 - checksum) % 10
-}
-
enum class TwinCardNumber(val number: Int) {
First(1), Second(2);
@@ -90,20 +55,14 @@ enum class TwinCardNumber(val number: Int) {
}
}
-fun Card.isTwinCard(): Boolean {
- return this.cardData?.productMask?.contains(Product.TwinCard) == true
+fun Card.isTangemTwin(): Boolean {
+ return TwinsHelper.getTwinCardNumber(cardId) != null
+}
+
+fun Card.getTwinCardNumber(): TwinCardNumber? {
+ return TwinsHelper.getTwinCardNumber(this.cardId)
}
fun Card.getTwinCardIdForUser(): String {
return TwinsHelper.getTwinCardIdForUser(this.cardId)
-}
-
-fun Card.changeStatusToLoaded(): Card {
- val wallets = wallets.map { it.copy(status = WalletStatus.Loaded) }
- return copy(status = CardStatus.Loaded, wallets = wallets)
-}
-
-fun Card.changeStatusToEmpty(): Card {
- val wallets = wallets.map { it.copy(status = WalletStatus.Empty) }
- return copy(status = CardStatus.Empty, wallets = wallets)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt
index bf6c735be5..94ce765f12 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt
@@ -1,49 +1,61 @@
package com.tangem.tap.domain.twins
-import com.tangem.CardSession
-import com.tangem.CardSessionRunnable
-import com.tangem.KeyPair
-import com.tangem.TangemSdkError
-import com.tangem.commands.*
import com.tangem.common.CompletionResult
-import com.tangem.common.TangemSdkConstants
+import com.tangem.common.KeyPair
+import com.tangem.common.SuccessResponse
+import com.tangem.common.core.CardSession
+import com.tangem.common.core.CardSessionRunnable
+import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.guard
import com.tangem.common.files.FileHashHelper
-import com.tangem.tap.domain.extensions.getDefaultWalletIndex
+import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
+import com.tangem.operations.issuerAndUserData.ReadIssuerDataResponse
+import com.tangem.operations.issuerAndUserData.WriteIssuerDataCommand
+import com.tangem.operations.sign.SignHashCommand
+import com.tangem.tap.domain.extensions.getSingleWallet
class WriteProtectedIssuerDataTask(
- private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair
-) : CardSessionRunnable {
- override val requiresPin2 = true
+ private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair,
+) : CardSessionRunnable {
- override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) {
- val hashes = arrayOf(twinPublicKey.calculateSha256())
- SignCommand(hashes, TangemSdkConstants.getDefaultWalletIndex()).run(session) { signResult ->
- when (signResult) {
- is CompletionResult.Success -> {
- ReadIssuerDataCommand().run(session) { readResult ->
- when (readResult) {
- is CompletionResult.Success -> {
- writeIssuerData(
- twinPublicKey, issuerKeys, signResult.data.signatures[0],
+ override fun run(
+ session: CardSession,
+ callback: (result: CompletionResult) -> Unit,
+ ) {
+ SignHashCommand(
+ twinPublicKey.calculateSha256(),
+ session.environment.card!!.getSingleWallet()!!.publicKey
+ )
+ .run(session) { signResult ->
+ when (signResult) {
+ is CompletionResult.Success -> {
+ ReadIssuerDataCommand().run(session) { readResult ->
+ when (readResult) {
+ is CompletionResult.Success -> {
+ writeIssuerData(
+ twinPublicKey, issuerKeys, signResult.data.signature,
readResult.data, session, callback
- )
+ )
+ }
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(
+ readResult.error))
}
- is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error))
}
}
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(signResult.error))
}
- is CompletionResult.Failure -> callback(CompletionResult.Failure(signResult.error))
}
- }
}
private fun writeIssuerData(
- twinPublicKey: ByteArray, issuerKeys: KeyPair, cardSignature: ByteArray,
- readResponse: ReadIssuerDataResponse,
- session: CardSession, callback: (result: CompletionResult
- ) -> Unit) {
+ twinPublicKey: ByteArray, issuerKeys: KeyPair, cardSignature: ByteArray,
+ readResponse: ReadIssuerDataResponse,
+ session: CardSession,
+ callback: (
+ result: CompletionResult,
+ ) -> Unit,
+ ) {
val cardId = session.environment.card?.cardId.guard {
callback(CompletionResult.Failure(TangemSdkError.CardError()))
return
@@ -51,11 +63,11 @@ class WriteProtectedIssuerDataTask(
val counter = (readResponse.issuerDataCounter ?: 0) + 1
val data = twinPublicKey + cardSignature
val signedByIssuer = FileHashHelper.prepareHashes(
- cardId, data, counter, issuerKeys.privateKey
+ cardId, data, counter, issuerKeys.privateKey
)
WriteIssuerDataCommand(
- data, signedByIssuer.finalizingSignature!!,
- counter, issuerKeys.publicKey
+ data, signedByIssuer.finalizingSignature!!,
+ counter, issuerKeys.publicKey
).run(session, callback)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt
index 44eece7399..f93217e447 100644
--- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt
@@ -3,6 +3,7 @@ package com.tangem.tap.domain.walletconnect
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.guard
import com.tangem.common.extensions.hexToBytes
+import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.details.redux.walletconnect.*
@@ -15,11 +16,9 @@ import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
import com.trustwallet.walletconnect.models.session.WCSession
import com.trustwallet.walletconnect.models.session.WCSessionUpdate
+import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-import okhttp3.Interceptor
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import okhttp3.Response
+import okhttp3.*
import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber
import java.util.*
@@ -29,9 +28,9 @@ class WalletConnectManager {
private val okHttpClient: OkHttpClient by lazy {
OkHttpClient.Builder()
- .connectTimeout(30, TimeUnit.SECONDS)
- .readTimeout(30, TimeUnit.SECONDS)
- .writeTimeout(30, TimeUnit.SECONDS)
+ .connectTimeout(20, TimeUnit.SECONDS)
+ .readTimeout(20, TimeUnit.SECONDS)
+ .writeTimeout(20, TimeUnit.SECONDS)
.addInterceptor(interceptor)
.addInterceptor(RetryInterceptor())
.build()
@@ -59,6 +58,18 @@ class WalletConnectManager {
client = client,
wallet = wallet
)
+ setupConnectionTimeoutCheck(session)
+ }
+
+ private fun setupConnectionTimeoutCheck(session: WCSession) {
+ scope.launch {
+ delay(20_000)
+ val data = sessions[session]
+ if (data != null && data.peerMeta == null) {
+ disconnect(session)
+ store.dispatchOnMain(WalletConnectAction.OpeningSessionTimeout(session))
+ }
+ }
}
fun restoreSessions() {
@@ -136,6 +147,10 @@ class WalletConnectManager {
}
private fun onSessionClosed(session: WCSession) {
+ FirebaseAnalyticsHandler.logWcEvent(
+ FirebaseAnalyticsHandler.WcAnalyticsEvent.Session(
+ FirebaseAnalyticsHandler.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url)
+ )
sessions.remove(session)
walletConnectRepository.removeSession(session)
store.dispatchOnMain(WalletConnectAction.RemoveSession(session))
@@ -184,7 +199,6 @@ class WalletConnectManager {
acceptRequest(data.session.session, data.id, hash)
sessions[data.session.session] = activeData.copy(transactionData = null)
}
-
}
fun handlePersonalSignRequest(
@@ -235,6 +249,10 @@ class WalletConnectManager {
store.dispatchOnMain(WalletConnectAction.AcceptOpeningSession(
sessionData))
}
+ FirebaseAnalyticsHandler.logWcEvent(
+ FirebaseAnalyticsHandler.WcAnalyticsEvent.Session(
+ FirebaseAnalyticsHandler.WcSessionEvent.Connect, peer.url)
+ )
}
}
client.onSessionUpdate = { id: Long, update: WCSessionUpdate ->
@@ -244,6 +262,11 @@ class WalletConnectManager {
}
client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction ->
Timber.d("onEthSendTransaction: $transaction")
+ FirebaseAnalyticsHandler.logWcEvent(
+ FirebaseAnalyticsHandler.WcAnalyticsEvent.Action(
+ FirebaseAnalyticsHandler.WcAction.SendTransaction
+ )
+ )
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(WalletConnectAction.HandleTransactionRequest(
transaction = transaction,
@@ -252,10 +275,14 @@ class WalletConnectManager {
type = WcTransactionType.EthSendTransaction
))
}
-
}
client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction ->
Timber.d("onEthSignTransaction: $transaction")
+ FirebaseAnalyticsHandler.logWcEvent(
+ FirebaseAnalyticsHandler.WcAnalyticsEvent.Action(
+ FirebaseAnalyticsHandler.WcAction.SignTransaction
+ )
+ )
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(WalletConnectAction.HandleTransactionRequest(
transaction = transaction,
@@ -267,6 +294,11 @@ class WalletConnectManager {
}
client.onEthSign = { id: Long, message: WCEthereumSignMessage ->
Timber.d("onEthSign: $message")
+ FirebaseAnalyticsHandler.logWcEvent(
+ FirebaseAnalyticsHandler.WcAnalyticsEvent.Action(
+ FirebaseAnalyticsHandler.WcAction.PersonalSign
+ )
+ )
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(WalletConnectAction.HandlePersonalSignRequest(
message,
diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt
index a78b81d7ce..e2ac447999 100644
--- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt
+++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt
@@ -10,10 +10,11 @@ import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.TransactionSender
import com.tangem.blockchain.extensions.*
-import com.tangem.commands.SignCommand
-import com.tangem.commands.wallet.WalletIndex
import com.tangem.common.CompletionResult
+import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
+import com.tangem.operations.sign.SignHashCommand
+import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.toFormattedString
import com.tangem.tap.features.details.redux.walletconnect.*
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData
@@ -60,7 +61,7 @@ class WalletConnectSdkHelper {
val decimals = blockchain.decimals()
- val value = transaction.value?.hexToBigDecimal()
+ val value = (transaction.value ?: "0").hexToBigDecimal()
?.movePointLeft(decimals) ?: return null
val gasPrice = transaction.gasPrice?.hexToBigDecimal()
@@ -128,6 +129,12 @@ class WalletConnectSdkHelper {
HEX_PREFIX + data.walletManager.wallet.recentTransactions.last().hash
}
is SimpleResult.Failure -> {
+ (result.error as? TangemSdkError)?.let { error ->
+ FirebaseAnalyticsHandler.logCardSdkError(
+ error,
+ FirebaseAnalyticsHandler.ActionToLog.WalletConnectTransaction,
+ )
+ }
Timber.e(result.error)
null
}
@@ -142,9 +149,9 @@ class WalletConnectSdkHelper {
gasLimit = null
) ?: return null
- val command = SignCommand(
- hashes = arrayOf(dataToSign.hash),
- walletIndex = WalletIndex.PublicKey(data.walletManager.wallet.publicKey)
+ val command = SignHashCommand(
+ hash = dataToSign.hash,
+ walletPublicKey = data.walletManager.wallet.publicKey
)
val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message())
return when (result) {
@@ -152,6 +159,12 @@ class WalletConnectSdkHelper {
HEX_PREFIX + result.data
}
is CompletionResult.Failure -> {
+ (result.error as? TangemSdkError)?.let { error ->
+ FirebaseAnalyticsHandler.logCardSdkError(
+ error,
+ FirebaseAnalyticsHandler.ActionToLog.WalletConnectSign,
+ )
+ }
Timber.e(result.error.customMessage)
null
}
@@ -196,18 +209,21 @@ class WalletConnectSdkHelper {
suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? {
val publicKey = wallet.walletPublicKey.hexToBytes()
- val command = SignCommand(
- arrayOf(hashToSign),
- WalletIndex.PublicKey(publicKey))
+ val command = SignHashCommand(hashToSign, publicKey)
return when (val result = tangemSdkManager.runTaskAsync(command, wallet.cardId)) {
is CompletionResult.Success -> {
- val hash = result.data.signatures.first()
-
+ val hash = result.data.signature
return EthereumUtils.prepareSignedMessageData(
hash, hashToSign, publicKey
)
}
is CompletionResult.Failure -> {
+ (result.error as? TangemSdkError)?.let { error ->
+ FirebaseAnalyticsHandler.logCardSdkError(
+ error,
+ FirebaseAnalyticsHandler.ActionToLog.WalletConnectSign,
+ )
+ }
Timber.e(result.error.customMessage)
null
}
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt
index 756d836287..74b37f90f4 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt
@@ -3,10 +3,11 @@ package com.tangem.tap.features.details.redux
import android.content.Context
import com.tangem.Message
import com.tangem.blockchain.common.Wallet
-import com.tangem.commands.common.card.Card
+import com.tangem.common.card.Card
+import com.tangem.operations.pins.CheckUserCodesResponse
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.common.redux.global.FiatCurrencyName
-import com.tangem.tap.domain.tasks.ScanNoteResponse
+import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.termsOfUse.CardTou
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.features.details.redux.twins.CreateTwinWallet
@@ -17,13 +18,13 @@ import org.rekotlin.Action
sealed class DetailsAction : Action {
data class PrepareScreen(
- val card: Card,
- val scanNoteResponse: ScanNoteResponse,
- val wallets: List,
- val isCreatingTwinWalletAllowed: Boolean?,
- val cardTou: CardTou,
- val fiatCurrencyName: FiatCurrencyName,
- val fiatCurrencies: List? = null,
+ val card: Card,
+ val scanResponse: ScanResponse,
+ val wallets: List,
+ val isCreatingTwinWalletAllowed: Boolean?,
+ val cardTou: CardTou,
+ val fiatCurrencyName: FiatCurrencyName,
+ val fiatCurrencies: List? = null,
) : DetailsAction()
object ShowDisclaimer : DetailsAction()
@@ -80,7 +81,7 @@ sealed class DetailsAction : Action {
}
data class LaunchThirdStep(val message: Message) : CreateTwinWalletAction() {
- data class Success(val scanNoteResponse: ScanNoteResponse) : CreateTwinWalletAction()
+ data class Success(val scanResponse: ScanResponse) : CreateTwinWalletAction()
object Failure : CreateTwinWalletAction()
}
}
@@ -93,6 +94,8 @@ sealed class DetailsAction : Action {
}
sealed class ManageSecurity : DetailsAction() {
+ data class CheckCurrentSecurityOption(val cardId: String?) : ManageSecurity()
+ data class SetCurrentOption(val userCodes: CheckUserCodesResponse) : ManageSecurity()
object OpenSecurity : ManageSecurity()
data class SelectOption(val option: SecurityOption) : ManageSecurity()
object SaveChanges : ManageSecurity() {
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt
index b66aa958e9..15eca00858 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt
@@ -1,7 +1,10 @@
package com.tangem.tap.features.details.redux
-import com.tangem.commands.common.network.Result
import com.tangem.common.CompletionResult
+import com.tangem.common.core.TangemSdkError
+import com.tangem.common.services.Result
+import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
+import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
@@ -81,15 +84,23 @@ class DetailsMiddleware {
store.dispatch(NavigationAction.PopBackTo())
}
is DetailsAction.EraseWallet.Confirm -> {
+ val card = store.state.detailsState.card ?: return
scope.launch {
- val result = tangemSdkManager.eraseWallet(
- store.state.detailsState.card?.cardId
- )
+ val result = tangemSdkManager.eraseWallet(card)
withContext(Dispatchers.Main) {
when (result) {
is CompletionResult.Success -> {
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
+ is CompletionResult.Failure -> {
+ (result.error as? TangemSdkError)?.let { error ->
+ FirebaseAnalyticsHandler.logCardSdkError(
+ error,
+ FirebaseAnalyticsHandler.ActionToLog.PurgeWallet,
+ card = store.state.detailsState.card
+ )
+ }
+ }
}
}
}
@@ -113,6 +124,21 @@ class DetailsMiddleware {
class ManageSecurityMiddleware {
fun handle(action: DetailsAction.ManageSecurity) {
when (action) {
+ is DetailsAction.ManageSecurity.CheckCurrentSecurityOption -> {
+ scope.launch {
+ when (val response = tangemSdkManager.checkUserCodes(action.cardId)) {
+ is CompletionResult.Success -> {
+ store.dispatchOnMain(
+ DetailsAction.ManageSecurity.SetCurrentOption(response.data)
+ )
+ store.dispatchOnMain(DetailsAction.ManageSecurity.OpenSecurity)
+ }
+ is CompletionResult.Failure -> {
+
+ }
+ }
+ }
+ }
is DetailsAction.ManageSecurity.OpenSecurity -> {
store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsSecurity))
}
@@ -148,8 +174,20 @@ class DetailsMiddleware {
}
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Success)
}
- is CompletionResult.Failure, null ->
+ is CompletionResult.Failure -> {
+ (result.error as? TangemSdkError)?.let { error ->
+ FirebaseAnalyticsHandler.logCardSdkError(
+ error = error,
+ actionToLog = FirebaseAnalyticsHandler.ActionToLog.ChangeSecOptions,
+ parameters = mapOf(
+ FirebaseAnalyticsHandler.AnalyticsParam.NEW_SECURITY_OPTION to
+ (selectedOption?.name ?: "")
+ ),
+ card = store.state.detailsState.card
+ )
+ }
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Failure)
+ }
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt
index 33c71239fb..d2bb330e40 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt
@@ -1,18 +1,17 @@
package com.tangem.tap.features.details.redux
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.common.card.masks.Settings
+import com.tangem.common.card.Card
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.TapWorkarounds.isStart2Coin
import com.tangem.tap.domain.extensions.isWalletDataSupported
import com.tangem.tap.domain.extensions.signedHashesCount
import com.tangem.tap.domain.extensions.toSendableAmounts
-import com.tangem.tap.domain.twins.TwinsHelper
-import com.tangem.tap.domain.twins.isTwinCard
+import com.tangem.tap.domain.twins.getTwinCardNumber
+import com.tangem.tap.domain.twins.isTangemTwin
import com.tangem.tap.features.details.redux.twins.CreateTwinWalletReducer
import com.tangem.tap.features.details.redux.twins.CreateTwinWalletState
-import com.tangem.tap.features.wallet.models.toPendingTransactions
+import com.tangem.tap.features.wallet.models.hasPendingTransactions
import org.rekotlin.Action
import java.util.*
@@ -47,21 +46,10 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
}
private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState {
- val securityOption = when {
- action.card.isPin1Default == false -> {
- SecurityOption.AccessCode
- }
- action.card.isPin2Default == false -> {
- SecurityOption.PassCode
- }
- else -> {
- SecurityOption.LongTap
- }
- }
- val twinsState = if (action.card.isTwinCard()) {
+ val twinsState = if (action.card.isTangemTwin()) {
CreateTwinWalletState(
- scanResponse = action.scanNoteResponse,
- twinCardNumber = TwinsHelper.getTwinCardNumber(action.card.cardId),
+ scanResponse = action.scanResponse,
+ twinCardNumber = action.card.getTwinCardNumber(),
createTwinWallet = null,
showAlert = false,
allowRecreatingWallet = action.isCreatingTwinWalletAllowed
@@ -76,7 +64,6 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta
appCurrencyState = AppCurrencyState(
action.fiatCurrencyName
),
- securityScreenState = SecurityScreenState(currentOption = securityOption),
createTwinWalletState = twinsState,
cardTermsOfUseUrl = action.cardTou.getUrl(action.card)
)
@@ -85,13 +72,10 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta
private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsState): DetailsState {
return when (action) {
DetailsAction.EraseWallet.Check -> {
- val notAllowedByCard =
- state.card?.settingsMask?.contains(Settings.ProhibitPurgeWallet) == true
- || state.card?.settingsMask?.contains(Settings.IsReusable) == false
- || state.card?.isWalletDataSupported == true
+ val notAllowedByAnyWallet = state.card?.wallets?.any { it.settings.isPermanent } ?: false
+ val notAllowedByCard = notAllowedByAnyWallet || state.card?.isWalletDataSupported == true
val notEmpty = state.wallets.any {
- !it.recentTransactions.toPendingTransactions(it.address).isNullOrEmpty()
- || it.amounts.toSendableAmounts().isNotEmpty()
+ it.hasPendingTransactions() || it.amounts.toSendableAmounts().isNotEmpty()
}
val eraseWalletState = when {
notAllowedByCard -> EraseWalletState.NotAllowedByCard
@@ -142,6 +126,20 @@ private fun handleSecurityAction(
action: DetailsAction.ManageSecurity, state: DetailsState
): DetailsState {
return when (action) {
+ is DetailsAction.ManageSecurity.SetCurrentOption -> {
+ val securityOption = when {
+ action.userCodes.isAccessCodeSet -> {
+ SecurityOption.AccessCode
+ }
+ action.userCodes.isPasscodeSet -> {
+ SecurityOption.PassCode
+ }
+ else -> {
+ SecurityOption.LongTap
+ }
+ }
+ state.copy(securityScreenState = SecurityScreenState(currentOption = securityOption))
+ }
is DetailsAction.ManageSecurity.OpenSecurity -> {
if (state.card?.isStart2Coin == true) {
return state.copy(securityScreenState = state.securityScreenState?.copy(
@@ -149,13 +147,10 @@ private fun handleSecurityAction(
selectedOption = state.securityScreenState.currentOption
))
}
- if (state.card?.isPin2Default == null) {
- return state.copy(securityScreenState = state.securityScreenState?.copy(
- allowedOptions = EnumSet.noneOf(SecurityOption::class.java)
- ))
- }
- val allowedSecurityOptions = prepareAllowedSecurityOptions(state.card)
+ val allowedSecurityOptions = prepareAllowedSecurityOptions(
+ state.card, state.securityScreenState?.currentOption
+ )
state.copy(securityScreenState = state.securityScreenState?.copy(
allowedOptions = allowedSecurityOptions,
selectedOption = state.securityScreenState.currentOption
@@ -176,40 +171,38 @@ private fun handleSecurityAction(
}
is DetailsAction.ManageSecurity.SaveChanges.Success -> {
// Setting options to show only LongTap from now on for non-twins
- val card = if (state.card?.isTwinCard() == true) {
- state.card
- } else {
- state.card?.copy(isPin1Default = true, isPin2Default = true)
- }
state.copy(
- card = card,
- securityScreenState = state.securityScreenState?.copy(
- currentOption = state.securityScreenState.selectedOption,
- allowedOptions = card?.let { prepareAllowedSecurityOptions(it) }
- ?: EnumSet.of(SecurityOption.LongTap)
- ))
+ securityScreenState = state.securityScreenState?.copy(
+ currentOption = state.securityScreenState.selectedOption,
+ allowedOptions = state.card?.let {
+ prepareAllowedSecurityOptions(
+ it, state.securityScreenState.selectedOption
+ )
+ } ?: EnumSet.of(SecurityOption.LongTap)
+ ))
}
else -> state
}
}
-private fun prepareAllowedSecurityOptions(card: Card): EnumSet {
- val prohibitDefaultPin = card.settingsMask?.contains(Settings.ProhibitDefaultPIN1) == true
- val isDefaultPin1 = card.isPin1Default != false
- val isDefaultPin2 = card.isPin2Default != false
+private fun prepareAllowedSecurityOptions(
+ card: Card?, currentSecurityOption: SecurityOption?
+): EnumSet {
+ val prohibitDefaultPin = card?.settings?.isResettingUserCodesAllowed != true
val allowedSecurityOptions = EnumSet.noneOf(SecurityOption::class.java)
- if (card.isTwinCard()) {
+
+ if (card?.isTangemTwin() == true) {
allowedSecurityOptions.add(SecurityOption.PassCode)
}
- if ((isDefaultPin1 && isDefaultPin2) || !prohibitDefaultPin) {
+ if ((currentSecurityOption == SecurityOption.LongTap) || !prohibitDefaultPin) {
allowedSecurityOptions.add(SecurityOption.LongTap)
}
- if (!isDefaultPin1) {
+ if (currentSecurityOption == SecurityOption.AccessCode) {
allowedSecurityOptions.add(SecurityOption.AccessCode)
}
- if (!isDefaultPin2) {
+ if (currentSecurityOption == SecurityOption.PassCode) {
allowedSecurityOptions.add(SecurityOption.PassCode)
}
return allowedSecurityOptions
@@ -218,6 +211,6 @@ private fun prepareAllowedSecurityOptions(card: Card): EnumSet {
private fun Card.toCardInfo(): CardInfo? {
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
- val issuer = this.cardData?.issuerName ?: return null
+ val issuer = this.issuer.name
val signedHashes = this.signedHashesCount()
return CardInfo(cardId, issuer, signedHashes)}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt
index 5ef1033e2f..bf16dd3674 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt
@@ -2,7 +2,7 @@ package com.tangem.tap.features.details.redux
import android.net.Uri
import com.tangem.blockchain.common.Wallet
-import com.tangem.commands.common.card.Card
+import com.tangem.common.card.Card
import com.tangem.tap.common.entities.Button
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
import com.tangem.tap.common.redux.global.FiatCurrencyName
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt
index fa81ea9a18..1a6c05c2b1 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt
@@ -48,7 +48,7 @@ class CreateTwinWalletMiddleware {
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
is DetailsAction.CreateTwinWalletAction.LaunchFirstStep -> {
- store.state.globalState.scanNoteResponse?.let {
+ store.state.globalState.scanResponse?.let {
twinsManager = TwinCardsManager(it, action.context)
}
scope.launch {
@@ -106,7 +106,7 @@ class CreateTwinWalletMiddleware {
}
is DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Success -> {
scope.launch {
- store.state.globalState.tapWalletManager.onCardScanned(action.scanNoteResponse)
+ store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse)
}
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt
index 488ac431bc..22c96dd0bf 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt
@@ -1,15 +1,15 @@
package com.tangem.tap.features.details.redux.twins
-import com.tangem.tap.domain.tasks.ScanNoteResponse
+import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.twins.TwinCardNumber
data class CreateTwinWalletState(
- val scanResponse: ScanNoteResponse?,
- val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep,
- val twinCardNumber: TwinCardNumber?,
- val createTwinWallet: CreateTwinWallet?,
- val showAlert: Boolean,
- val allowRecreatingWallet: Boolean? = null
+ val scanResponse: ScanResponse?,
+ val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep,
+ val twinCardNumber: TwinCardNumber?,
+ val createTwinWallet: CreateTwinWallet?,
+ val showAlert: Boolean,
+ val allowRecreatingWallet: Boolean? = null
)
enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep }
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt
index 728a8d0446..16a3f638a4 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt
@@ -31,6 +31,8 @@ sealed class WalletConnectAction : Action {
object RefuseOpeningSession : WalletConnectAction()
+ data class OpeningSessionTimeout(val session: WCSession) : WalletConnectAction()
+
data class AcceptOpeningSession(val session: WalletConnectSession) : WalletConnectAction()
data class ApproveSession(
@@ -41,7 +43,6 @@ sealed class WalletConnectAction : Action {
data class FailureEstablishingSession(val session: WCSession?) : WalletConnectAction()
-
data class SetSessionsRestored(val sessions: List) :
WalletConnectAction()
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt
index 89bec71f1f..dff6c35c72 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt
@@ -1,25 +1,22 @@
package com.tangem.tap.features.details.redux.walletconnect
import com.tangem.blockchain.common.*
-import com.tangem.commands.common.card.Card
-import com.tangem.common.CompletionResult
+import com.tangem.common.card.Card
+import com.tangem.common.extensions.guard
import com.tangem.common.extensions.toHexString
import com.tangem.tap.*
-import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.getFromClipboard
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
import com.tangem.tap.domain.isMultiwalletAllowed
import com.tangem.tap.domain.walletconnect.WalletConnectManager
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.wallet.R
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import org.rekotlin.Middleware
class WalletConnectMiddleware {
@@ -60,6 +57,10 @@ class WalletConnectMiddleware {
handleScanCard(action.wcUri)
}
+ is WalletConnectAction.OpeningSessionTimeout -> {
+ store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.SessionTimeout))
+ }
+
is WalletConnectAction.FailureEstablishingSession -> {
if (action.session != null) {
walletConnectManager.disconnect(action.session)
@@ -127,49 +128,35 @@ class WalletConnectMiddleware {
}
private fun handleScanCard(wcUri: String) {
- scope.launch {
- val result = tangemSdkManager.scanNote(
- FirebaseAnalyticsHandler,
- R.string.wallet_connect_scan_card_message
- )
- withContext(Dispatchers.Main) {
- when (result) {
- is CompletionResult.Success -> {
- val card = result.data.card
-
- if (!card.isMultiwalletAllowed) {
- store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
- return@withContext
- }
-
- val walletManager = getWalletManager(card)
- if (walletManager == null) {
- store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
- return@withContext
- }
-
- val key = walletManager.wallet.publicKey
- store.dispatchOnMain(WalletConnectAction.OpenSession(
- wcUri = wcUri,
- wallet = WalletForSession(
- card.cardId, key.toHexString(),
- isTestNet = false
- ),
- ))
- }
- is CompletionResult.Failure ->
- store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(
- null
- ))
- }
+ store.dispatch(GlobalAction.ScanCard({ scanResponse ->
+ val card = scanResponse.card
+ if (!card.isMultiwalletAllowed) {
+ store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
+ return@ScanCard
}
- }
+
+ val walletManager = getWalletManager(card).guard {
+ store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
+ return@ScanCard
+ }
+
+ val key = walletManager.wallet.publicKey
+ store.dispatchOnMain(WalletConnectAction.OpenSession(
+ wcUri = wcUri,
+ wallet = WalletForSession(
+ card.cardId, key.toHexString(),
+ isTestNet = card.isTestCard
+ ),
+ ))
+ }, {
+ store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null))
+ }, R.string.wallet_connect_scan_card_message))
}
private fun getWalletManager(card: Card): WalletManager? {
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
- return if (store.state.globalState.scanNoteResponse?.card?.cardId == card.cardId) {
+ return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) {
store.state.walletState.getWalletManager(Blockchain.Ethereum)
?: factory.makeWalletManagerForApp(card, Blockchain.Ethereum)
?.also { walletManager ->
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt
index df3eec9834..c8b6f3b1b7 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt
@@ -28,6 +28,7 @@ class WalletConnectReducer {
is WalletConnectAction.UnsupportedCard -> state.copy(loading = false)
is WalletConnectAction.RefuseOpeningSession -> state.copy(loading = false)
+ is WalletConnectAction.OpeningSessionTimeout -> state.copy(loading = false)
is WalletConnectAction.FailureEstablishingSession -> state.copy(loading = false)
else -> state
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt
index f5b7ce0c5f..38aeaabc84 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt
@@ -1,10 +1,8 @@
package com.tangem.tap.features.details.redux.walletconnect
-import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.WalletManager
-import com.tangem.common.extensions.hexToBytes
-import com.tangem.tap.common.redux.global.StateDialog
+import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData
import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData
import com.trustwallet.walletconnect.models.WCPeerMeta
@@ -30,11 +28,6 @@ data class WalletForSession(
) {
val chainId
get() = if (isTestNet) 4 else 1
-
- fun getAddress(): String {
- val blockchain = if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum
- return blockchain.makeAddresses(walletPublicKey.hexToBytes()).first().value
- }
}
@@ -42,6 +35,7 @@ sealed class WalletConnectDialog : StateDialog {
data class ClipboardOrScanQr(val clipboardUri: String) : WalletConnectDialog()
object UnsupportedCard : WalletConnectDialog()
object OpeningSessionRejected : WalletConnectDialog()
+ object SessionTimeout : WalletConnectDialog()
data class ApproveWcSession(val session: WalletConnectSession) : WalletConnectDialog()
data class RequestTransaction(val dialogData: TransactionRequestDialogData) :
WalletConnectDialog()
diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt
index 3b0e9c83a1..640cc47cf0 100644
--- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt
@@ -12,7 +12,7 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.twins.getTwinCardIdForUser
-import com.tangem.tap.domain.twins.isTwinCard
+import com.tangem.tap.domain.twins.isTangemTwin
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.details.redux.DetailsState
import com.tangem.tap.features.details.redux.SecurityOption
@@ -68,7 +68,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber
+ dialogData.positiveButton.action()
+ }
+ setNegativeButton(dialogData.negativeButton.title) { _, _ ->
+ dialogData.negativeButton.action()
+ }
+ setOnDismissListener { dialogData.onDismissAction() }
+ }.create()
+ }
+ }
+}
+
+data class DialogData(
+ val title: String,
+ val message: String,
+ val positiveButton: ButtonData,
+ val negativeButton: ButtonData,
+ val onDismissAction: () -> Unit,
+)
+
+data class ButtonData(
+ val title: String,
+ val action: () -> Unit,
+)
+
+//class WcDialogMessageBuilder(val messageRes: Int, val data: DialogMessageData? = null) {
+// fun build(context: Context): String {
+// when (data) {
+// null -> context.getString(messageRes)
+// }
+// }
+//}
+
+interface DialogMessageData
+
+data class WcTransactionDialogMessageData(
+ val cardId: String,
+ val dAppName: String,
+ val dAppUrl: String,
+ val amount: String,
+ val gasAmount: String,
+ val totalAmount: String,
+ val balance: String,
+ val isEnoughFundsToSend: Boolean,
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt
index 066b2b83db..12a1cfaccd 100644
--- a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerMiddleware.kt
@@ -1,8 +1,9 @@
package com.tangem.tap.features.disclaimer.redux
+import com.tangem.tap.common.post
import com.tangem.tap.common.redux.AppState
-import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.preferencesStorage
import com.tangem.tap.store
import org.rekotlin.Middleware
@@ -14,16 +15,10 @@ class DisclaimerMiddleware {
when (action) {
is DisclaimerAction.AcceptDisclaimer -> {
preferencesStorage.saveDisclaimerAccepted()
- if (store.state.walletState.twinCardsState != null) {
- val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown()
- if (showOnboarding) {
- store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding))
- } else {
- store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
- }
- } else {
- store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
- }
+ store.dispatch(NavigationAction.PopBackTo())
+
+ //delayed sending used for better animation
+ post(550) { store.dispatch(HomeAction.ReadCard) }
}
}
next(action)
diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt
index fb7ebe023d..ec7a45dd3d 100644
--- a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt
@@ -58,6 +58,9 @@ class DisclaimerFragment : Fragment(R.layout.fragment_disclaimer),
override fun newState(state: DisclaimerState) {
if (activity == null) return
- if (state.accepted) btn_accept.hide()
+ if (state.accepted) {
+ half_transparent_overlay.hide()
+ btn_accept.hide()
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt b/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt
index bab36b6f67..6007d61e23 100644
--- a/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt
+++ b/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt
@@ -13,8 +13,7 @@ import androidx.core.content.FileProvider
import com.tangem.Log
import com.tangem.TangemSdkLogger
import com.tangem.blockchain.common.*
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.wallet.WalletStatus
+import com.tangem.common.card.Card
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.domain.TapWorkarounds
import timber.log.Timber
@@ -176,11 +175,10 @@ class AdditionalEmailInfo {
fun setCardInfo(card: Card) {
cardId = card.cardId
- cardFirmwareVersion = card.firmwareVersion.version
- cardIssuer = card.cardData?.issuerName ?: ""
+ cardFirmwareVersion = card.firmwareVersion.stringValue
+ cardIssuer = card.issuer.name
signedHashesCount = card.wallets
- .filter { it.status == WalletStatus.Loaded }
- .joinToString(";") { "${it.curve?.curve} - ${it.signedHashes}" }
+ .joinToString(";") { "${it.curve?.curve} - ${it.totalSignedHashes}" }
}
fun setWalletsInfo(walletManagers: List) {
diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt
index c93543613e..d7b0c6d941 100644
--- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt
@@ -1,26 +1,62 @@
package com.tangem.tap.features.home
-import android.app.Dialog
import android.os.Bundle
import android.view.View
-import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
-import com.tangem.tap.common.redux.global.StateDialog
+import com.tangem.tap.common.extensions.hide
+import com.tangem.tap.common.redux.navigation.FragmentShareTransition
+import com.tangem.tap.common.redux.navigation.ShareElement
+import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget
+import com.tangem.tap.common.toggleWidget.ViewStateWidget
+import com.tangem.tap.common.transitions.FrontCardEnterTransition
+import com.tangem.tap.common.transitions.FrontCardExitTransition
import com.tangem.tap.features.home.redux.HomeAction
-import com.tangem.tap.features.home.redux.HomeDialog
import com.tangem.tap.features.home.redux.HomeState
-import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
+import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
import com.tangem.tap.store
import com.tangem.wallet.R
-import kotlinx.android.synthetic.main.fragment_home.*
-import org.rekotlin.StoreSubscriber
+import kotlinx.android.synthetic.main.fragment_onboarding_main.*
+import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.*
+import kotlinx.android.synthetic.main.layout_onboarding_home.*
+import java.lang.ref.WeakReference
-class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber {
+class HomeFragment : BaseOnboardingFragment() {
- private var dialog: Dialog? = null
+ private lateinit var btnScanCard: ViewStateWidget
- override fun onStart() {
- super.onStart()
+ override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_home
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val inflater = TransitionInflater.from(requireContext())
+ exitTransition = inflater.inflateTransition(R.transition.fade)
+ store.dispatch(HomeAction.Init)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ toolbar.hide()
+ val shareTransition = FragmentShareTransition(
+ listOf(ShareElement(WeakReference(imv_front_card), imv_front_card.transitionName)),
+ FrontCardEnterTransition(),
+ FrontCardExitTransition()
+ )
+
+ store.dispatch(HomeAction.SetFragmentShareTransition(shareTransition))
+
+ tv_header.setText(R.string.home_welcome_header)
+ tv_body.setText(R.string.home_welcome_body)
+
+ btn_main_action.setText(R.string.home_button_scan)
+ btn_main_action.setOnClickListener { store.dispatch(HomeAction.ReadCard) }
+ btn_alternative_action.setText(R.string.home_button_get_new_card)
+ btn_alternative_action.setOnClickListener { store.dispatch(HomeAction.GoToShop) }
+
+ btnScanCard = IndeterminateProgressButtonWidget(btn_main_action, progress)
+ }
+
+ override fun subscribeToStore() {
store.subscribe(this) { state ->
state.skipRepeats { oldState, newState ->
oldState.homeState == newState.homeState
@@ -28,49 +64,15 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber {
- if (dialog == null) dialog = ScanFailsDialog.create(requireContext()).apply { show() }
- }
- else -> {
- dialog?.dismiss()
- dialog = null
- }
- }
- }
-
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- btn_yes?.setOnClickListener { store.dispatch(HomeAction.ReadCard) }
- btn_shop?.setOnClickListener { store.dispatch(HomeAction.GoToShop(requireContext())) }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val inflater = TransitionInflater.from(requireContext())
- exitTransition = inflater.inflateTransition(R.transition.fade)
+ override fun onDestroyView() {
+ store.dispatch(HomeAction.SetFragmentShareTransition(null))
+ super.onDestroyView()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt
index 4a575343c7..14f762f6c4 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt
@@ -1,18 +1,20 @@
package com.tangem.tap.features.home.redux
-import android.content.Context
+import com.tangem.tap.common.entities.IndeterminateProgressButton
+import com.tangem.tap.common.redux.navigation.FragmentShareTransition
import org.rekotlin.Action
sealed class HomeAction : Action {
+ // from ui
object ReadCard : HomeAction()
- data class GoToShop(val context: Context) : HomeAction()
- object CheckIfFirstLaunch : HomeAction() {
- data class Result(val firstLaunch: Boolean) : HomeAction()
- }
+ object GoToShop : HomeAction()
- object ShowDialog : HomeAction() {
- object ScanFails : HomeAction()
- }
+ // internal
+ data class ShouldScanCardOnResume(val shouldScanCard: Boolean):HomeAction()
+ object Init : HomeAction()
+
+ data class SetFragmentShareTransition(val shareTransition: FragmentShareTransition?) : HomeAction()
+ data class SetTermsOfUseState(val isDisclaimerAccepted: Boolean) : HomeAction()
+ data class ChangeScanCardButtonState(val state: IndeterminateProgressButton) : HomeAction()
- object HideDialog : HomeAction()
}
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt
index b084b208cb..46e097943a 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt
@@ -1,80 +1,83 @@
package com.tangem.tap.features.home.redux
-import android.content.Intent
-import android.net.Uri
-import androidx.core.content.ContextCompat.startActivity
-import com.tangem.common.CompletionResult
-import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
+import com.tangem.tap.common.entities.IndeterminateProgressButton
+import com.tangem.tap.common.extensions.dispatchOpenUrl
+import com.tangem.tap.common.post
+import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
+import com.tangem.tap.common.redux.navigation.FragmentShareTransition
import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE
+import com.tangem.tap.features.onboarding.OnboardingHelper
+import com.tangem.tap.features.send.redux.states.ButtonState
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
-import com.tangem.tap.tangemSdkManager
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import org.rekotlin.Middleware
class HomeMiddleware {
- val homeMiddleware: Middleware = { dispatch, state ->
- { next ->
- { action ->
- when (action) {
- is HomeAction.CheckIfFirstLaunch -> {
- store.dispatch(
- HomeAction.CheckIfFirstLaunch.Result(preferencesStorage.getCountOfLaunches() == 1)
- )
- }
- is HomeAction.ReadCard -> {
- scope.launch {
- val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler)
- withContext(Dispatchers.Main) {
- store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result))
- when (result) {
- is CompletionResult.Success -> {
- tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card)
- store.dispatch(GlobalAction.RestoreAppCurrency)
- store.state.globalState.tapWalletManager.onCardScanned(result.data)
- showDisclaimerOrNavigateToWallet()
- }
- }
- }
- }
- }
- is HomeAction.GoToShop -> {
- val uri = Uri.parse(CARD_SHOP_URI)
- val intent = Intent(Intent.ACTION_VIEW, uri)
- startActivity(action.context, intent, null)
- }
- }
- next(action)
- }
- }
- }
-
- private fun showDisclaimerOrNavigateToWallet() {
- if (!preferencesStorage.wasDisclaimerAccepted()) {
- store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
- return
- }
- if (store.state.walletState.twinCardsState != null) {
- val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown()
- if (showOnboarding) {
- store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding))
- return
- }
- }
- store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
- }
-
companion object {
- private const val CARD_SHOP_URI =
+ val handler = homeMiddleware
+
+ const val CARD_SHOP_URI =
"https://shop.tangem.com/?afmc=1i&utm_campaign=1i&utm_source=leaddyno&utm_medium=affiliate"
}
}
+private val homeMiddleware: Middleware = { dispatch, state ->
+ { next ->
+ { action ->
+ when (action) {
+ is HomeAction.Init -> {
+ store.dispatch(GlobalAction.RestoreAppCurrency)
+ store.dispatch(GlobalAction.GetMoonPayUserStatus)
+ store.dispatch(HomeAction.SetTermsOfUseState(preferencesStorage.wasDisclaimerAccepted()))
+ }
+ is HomeAction.ShouldScanCardOnResume -> {
+ if (action.shouldScanCard) {
+ store.dispatch(HomeAction.ShouldScanCardOnResume(false))
+ postUi(700) { store.dispatch(HomeAction.ReadCard) }
+ }
+ }
+ is HomeAction.ReadCard -> handleReadCard()
+ is HomeAction.GoToShop -> store.dispatchOpenUrl(HomeMiddleware.CARD_SHOP_URI)
+ }
+ next(action)
+ }
+ }
+}
+private fun handleReadCard() {
+ if (!preferencesStorage.wasDisclaimerAccepted()) {
+ store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
+ } else {
+ changeButtonState(ButtonState.PROGRESS)
+ store.dispatch(GlobalAction.ScanCard({ scanResponse ->
+ if (OnboardingHelper.isOnboardingCase(scanResponse)) {
+ val navigateTo = OnboardingHelper.whereToNavigate(scanResponse)
+ store.dispatch(GlobalAction.Onboarding.Start(scanResponse))
+ navigateTo(navigateTo, store.state.homeState.shareTransition)
+ } else {
+ scope.launch { store.state.globalState.tapWalletManager.onCardScanned(scanResponse) }
+ navigateTo(AppScreen.Wallet, null)
+ }
+ }, {
+ changeButtonState(ButtonState.ENABLED)
+ }))
+ }
+}
+private fun changeButtonState(state: ButtonState) {
+ val btnState = IndeterminateProgressButton(state)
+ store.dispatch(HomeAction.ChangeScanCardButtonState(btnState))
+}
+
+private fun navigateTo(screen: AppScreen, transition: FragmentShareTransition?) {
+ post(DELAY_SDK_DIALOG_CLOSE) {
+ changeButtonState(ButtonState.ENABLED)
+ store.dispatch(NavigationAction.NavigateTo(screen, transition))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt
index 3c031ed7b2..d4f11f31f9 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt
@@ -13,18 +13,18 @@ private fun internalReduce(action: Action, state: AppState): HomeState {
if (action !is HomeAction) return state.homeState
- var homeState = state.homeState
+ var state = state.homeState
when (action) {
- is HomeAction.CheckIfFirstLaunch.Result -> {
- homeState = homeState.copy(firstLaunch = action.firstLaunch)
+ is HomeAction.SetFragmentShareTransition -> {
+ state = state.copy(shareTransition = action.shareTransition)
}
- is HomeAction.ShowDialog.ScanFails -> {
- homeState = homeState.copy(dialog = HomeDialog.ScanFailsDialog)
+ is HomeAction.ShouldScanCardOnResume -> {
+ state = state.copy(shouldScanCardOnResume = action.shouldScanCard)
}
- is HomeAction.HideDialog -> {
- homeState = homeState.copy(dialog = null)
+ is HomeAction.ChangeScanCardButtonState -> {
+ state = state.copy(btnScanState = action.state)
}
}
- return homeState
+ return state
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt
index c9fce9b492..8c9949cf4c 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt
@@ -1,13 +1,12 @@
package com.tangem.tap.features.home.redux
-import com.tangem.tap.common.redux.global.StateDialog
+import com.tangem.tap.common.entities.IndeterminateProgressButton
+import com.tangem.tap.common.redux.navigation.FragmentShareTransition
+import com.tangem.tap.features.send.redux.states.ButtonState
import org.rekotlin.StateType
data class HomeState(
- val firstLaunch: Boolean = true,
- val dialog: StateDialog? = null
-) : StateType
-
-sealed class HomeDialog: StateDialog {
- object ScanFailsDialog: HomeDialog()
-}
\ No newline at end of file
+ val shouldScanCardOnResume: Boolean = false,
+ val shareTransition: FragmentShareTransition? = null,
+ val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED),
+) : StateType
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt
new file mode 100644
index 0000000000..0b5b229bc4
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt
@@ -0,0 +1,44 @@
+package com.tangem.tap.features.onboarding
+
+import android.content.Context
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.tangem.tap.common.extensions.copyToClipboard
+import com.tangem.tap.common.extensions.dispatchDialogHide
+import com.tangem.tap.common.redux.AppDialog
+import com.tangem.tap.store
+import com.tangem.wallet.R
+import kotlinx.android.synthetic.main.dialog_onboarding_address_info.*
+import kotlinx.android.synthetic.main.layout_pseudo_toolbar.*
+
+/**
+[REDACTED_AUTHOR]
+ */
+class AddressInfoBottomSheetDialog(
+ private val stateDialog: AppDialog.AddressInfoDialog,
+ context: Context
+) : BottomSheetDialog(context) {
+
+ init {
+ this.setContentView(R.layout.dialog_onboarding_address_info)
+ setOnCancelListener { store.dispatchDialogHide() }
+ }
+
+ override fun show() {
+ super.show()
+ val data = stateDialog.addressData
+
+ imv_close.setOnClickListener {
+ dismissWithAnimation = true
+ cancel()
+ }
+ imv_qr_code.setImageBitmap(data.qrCode)
+ tv_address.text = data.address
+ btn_fl_copy_address.setOnClickListener {
+ context.copyToClipboard(data.address)
+ stateDialog.onCopyAddress()
+ }
+ btn_fl_explore.setOnClickListener {
+ stateDialog.onExploreAddress()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt
new file mode 100644
index 0000000000..38b2517ad1
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt
@@ -0,0 +1,27 @@
+package com.tangem.tap.features.onboarding
+
+import com.tangem.tap.common.redux.navigation.AppScreen
+import com.tangem.tap.domain.ProductType
+import com.tangem.tap.domain.extensions.hasWallets
+import com.tangem.tap.domain.tasks.product.ScanResponse
+import com.tangem.tap.preferencesStorage
+
+/**
+[REDACTED_AUTHOR]
+ */
+class OnboardingHelper {
+ companion object {
+
+ fun isOnboardingCase(response: ScanResponse): Boolean = if (response.card.hasWallets())
+ preferencesStorage.usedCardsPrefStorage.activationIsStarted(response.card.cardId) else true
+
+ fun whereToNavigate(scanResponse: ScanResponse): AppScreen {
+ return when (scanResponse.productType) {
+ ProductType.Note -> AppScreen.OnboardingNote
+ ProductType.Wallet -> AppScreen.OnboardingWallet
+ ProductType.Twin -> AppScreen.OnboardingTwins
+ ProductType.Other -> AppScreen.OnboardingOther
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt
new file mode 100644
index 0000000000..2b4894d5b9
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt
@@ -0,0 +1,128 @@
+package com.tangem.tap.features.onboarding
+
+import com.tangem.blockchain.common.AmountType
+import com.tangem.blockchain.common.Blockchain
+import com.tangem.blockchain.common.WalletManager
+import com.tangem.common.extensions.isZero
+import com.tangem.common.services.Result
+import com.tangem.operations.attestation.CardVerifyAndGetInfo
+import com.tangem.operations.attestation.OnlineCardVerifier
+import com.tangem.tap.common.extensions.isPositive
+import com.tangem.tap.common.extensions.safeUpdate
+import com.tangem.tap.domain.TapError
+import com.tangem.tap.domain.UrlBitmapLoader
+import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl
+import com.tangem.tap.domain.tasks.product.ScanResponse
+import com.tangem.tap.features.wallet.models.hasPendingTransactions
+import com.tangem.tap.features.wallet.redux.Artwork
+import com.tangem.tap.features.wallet.redux.Currency
+import com.tangem.tap.features.wallet.redux.ProgressState
+import com.tangem.tap.persistence.UsedCardsPrefStorage
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import java.math.BigDecimal
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+/**
+[REDACTED_AUTHOR]
+ */
+class OnboardingManager(
+ var scanResponse: ScanResponse,
+ val usedCardsPrefStorage: UsedCardsPrefStorage,
+) {
+
+ var cardInfo: Result? = null
+ private set
+
+ suspend fun loadArtwork(): Artwork {
+ cardInfo = OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey)
+ return loadArtwork(scanResponse.card.getOrLoadCardArtworkUrl(cardInfo))
+ }
+
+ suspend fun loadArtwork(url: String): Artwork {
+ return withContext(Dispatchers.Main) {
+ suspendCoroutine { continuation ->
+ UrlBitmapLoader().loadBitmap(url) {
+ val result = when (it) {
+ is Result.Success -> Artwork(url, it.data)
+ is Result.Failure -> Artwork(url, null)
+ }
+ continuation.resume(result)
+ }
+ }
+ }
+ }
+
+ suspend fun updateBalance(walletManager: WalletManager): OnboardingWalletBalance {
+ return when (val result = walletManager.safeUpdate()) {
+ is Result.Success -> {
+ val wallet = walletManager.wallet
+ val valueOfAmount = wallet.amounts[AmountType.Coin]?.value
+ if (valueOfAmount == null) {
+ val customError = TapError.CustomError("Amount is NULL")
+ OnboardingWalletBalance.criticalError(customError)
+ } else {
+ val currency = Currency.Blockchain(walletManager.wallet.blockchain)
+ val balance = if (valueOfAmount.isZero()) {
+ OnboardingWalletBalance.done(valueOfAmount, wallet.hasPendingTransactions(), currency)
+ } else {
+ OnboardingWalletBalance.done(valueOfAmount, false, currency)
+ }
+ balance
+ }
+ }
+ is Result.Failure -> {
+ val error = (result.error as? TapError) ?: TapError.UnknownError
+ when (error) {
+ is TapError.WalletManagerUpdate.NoAccountError -> OnboardingWalletBalance.error(error)
+ // NoInternetConnection, WalletManagerUpdate.InternalError
+ else -> OnboardingWalletBalance.criticalError(error)
+ }
+ }
+ }
+ }
+
+ fun activationStarted(cardId: String) {
+ usedCardsPrefStorage.activationStarted(cardId)
+ }
+
+ fun activationFinished(cardId: String) {
+ usedCardsPrefStorage.activationFinished(cardId)
+ }
+}
+
+data class OnboardingWalletBalance(
+ val value: BigDecimal = BigDecimal.ZERO,
+ val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown),
+ val hasIncomingTransaction: Boolean = false,
+ val state: ProgressState,
+ val error: TapError? = null,
+ val criticalError: TapError? = null,
+) {
+
+ fun balanceIsToppedUp(): Boolean = value.isPositive() || hasIncomingTransaction
+
+ val amountToCreateAccount: String?
+ get() = if (error is TapError.WalletManagerUpdate.NoAccountError) error.customMessage else null
+
+ companion object {
+ fun error(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance(
+ state = ProgressState.Error,
+ error = error
+ )
+
+ fun criticalError(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance(
+ state = ProgressState.Error,
+ criticalError = error
+ )
+
+ fun loading(value: BigDecimal = BigDecimal.ZERO): OnboardingWalletBalance = OnboardingWalletBalance(
+ value,
+ state = ProgressState.Loading
+ )
+
+ fun done(value: BigDecimal, hasTransactions: Boolean, currency: Currency.Blockchain): OnboardingWalletBalance =
+ OnboardingWalletBalance(value, currency, hasTransactions, ProgressState.Done)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt
new file mode 100644
index 0000000000..7cdbff7733
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt
@@ -0,0 +1,46 @@
+package com.tangem.tap.features.onboarding.products
+
+import android.os.Bundle
+import android.view.View
+import com.tangem.tap.common.extensions.inflate
+import com.tangem.tap.common.extensions.show
+import com.tangem.tap.common.transitions.HomeToOnboardingTransition
+import com.tangem.tap.features.send.BaseStoreFragment
+import com.tangem.wallet.R
+import kotlinx.android.synthetic.main.fragment_onboarding_main.*
+import kotlinx.android.synthetic.main.view_confetti.*
+import org.rekotlin.StoreSubscriber
+
+/**
+[REDACTED_AUTHOR]
+ */
+abstract class BaseOnboardingFragment : BaseStoreFragment(R.layout.fragment_onboarding_main), StoreSubscriber {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ configureTransitions()
+ }
+
+ override fun configureTransitions() {
+ enterTransition = HomeToOnboardingTransition()
+ exitTransition = HomeToOnboardingTransition()
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ onboarding_top_container.inflate(getOnboardingTopContainerId(), true)
+ }
+
+ abstract fun getOnboardingTopContainerId(): Int
+
+ protected fun showConfetti(show: Boolean) {
+ lav_confetti.show(show)
+
+ if (show) {
+ lav_confetti.playAnimation()
+ } else {
+ lav_confetti.cancelAnimation()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt
new file mode 100644
index 0000000000..6bf7ca96bf
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt
@@ -0,0 +1,184 @@
+package com.tangem.tap.features.onboarding.products.note
+
+import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.view.View
+import android.view.animation.OvershootInterpolator
+import android.widget.ImageView
+import androidx.annotation.LayoutRes
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.view.isVisible
+import androidx.transition.TransitionManager
+import com.tangem.blockchain.common.Blockchain
+import com.tangem.tangem_sdk_new.extensions.fadeIn
+import com.tangem.tangem_sdk_new.extensions.fadeOut
+import com.tangem.tap.common.extensions.getDrawableCompat
+import com.tangem.tap.common.extensions.stripZeroPlainString
+import com.tangem.tap.common.postUi
+import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
+import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
+import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
+import com.tangem.tap.features.onboarding.products.note.redux.*
+import com.tangem.tap.store
+import com.tangem.wallet.R
+import kotlinx.android.synthetic.main.fragment_onboarding_main.*
+import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.*
+import kotlinx.android.synthetic.main.layout_onboarding_note.*
+import kotlinx.android.synthetic.main.view_onboarding_progress.*
+import kotlinx.android.synthetic.main.view_onboarding_tv_balance.*
+
+/**
+[REDACTED_AUTHOR]
+ */
+class OnboardingNoteFragment : BaseOnboardingFragment() {
+
+ private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
+ private var currentStep: OnboardingNoteStep = OnboardingNoteStep.None
+
+ override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ toolbar.setTitle(R.string.onboarding_title)
+ btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container)
+ val resources = AndroidResources(
+ OnboardingStringResources(
+ R.string.copy_toast_msg
+ )
+ )
+
+ store.dispatch(OnboardingNoteAction.LoadCardArtwork)
+ store.dispatch(OnboardingNoteAction.SetResources(resources))
+ store.dispatch(OnboardingNoteAction.DetermineStepOfScreen)
+ }
+
+ override fun subscribeToStore() {
+ store.subscribe(this) { state ->
+ state.skipRepeats { oldState, newState ->
+ oldState.onboardingNoteState == newState.onboardingNoteState
+ }.select { it.onboardingNoteState }
+ }
+ storeSubscribersList.add(this)
+ }
+
+ override fun newState(state: OnboardingNoteState) {
+ if (activity == null) return
+
+ state.cardArtwork?.let { imv_front_card.swapToBitmapDrawable(it.artwork) }
+ pb_state.max = state.steps.size - 1
+ pb_state.progress = state.progress
+
+ when (state.currentStep) {
+ OnboardingNoteStep.None -> setupNoneState(state)
+ OnboardingNoteStep.CreateWallet -> setupCreateWalletState(state)
+ OnboardingNoteStep.TopUpWallet -> setupTopUpWalletState(state)
+ OnboardingNoteStep.Done -> setupDoneState(state)
+ }
+ setBalance(state)
+ showConfetti(state.showConfetti)
+ currentStep = state.currentStep
+ }
+
+ private fun setBalance(state: OnboardingNoteState) {
+ if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return
+
+ val balanceValue = state.walletBalance.value.stripZeroPlainString()
+ val currency = state.walletBalance.currency.currencySymbol
+ tv_balance_value.text = "$balanceValue $currency"
+ }
+
+ private fun setupNoneState(state: OnboardingNoteState) {
+ btn_main_action.isVisible = false
+ btn_alternative_action.isVisible = false
+ btnRefreshBalanceWidget.show(false)
+ tv_header.isVisible = false
+ tv_body.isVisible = false
+
+ btn_main_action.text = ""
+ btn_alternative_action.text = ""
+ tv_header.text = ""
+ tv_body.text = ""
+ }
+
+ private fun setupCreateWalletState(state: OnboardingNoteState) {
+ btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet)
+ btn_main_action.setOnClickListener { store.dispatch(OnboardingNoteAction.CreateWallet) }
+ btn_alternative_action.setText(R.string.onboarding_button_what_does_it_mean)
+ btn_alternative_action.setOnClickListener { }
+
+ btnRefreshBalanceWidget.mainView.setOnClickListener(null)
+
+ tv_header.setText(R.string.onboarding_create_wallet_header)
+ tv_body.setText(R.string.onboarding_create_wallet_body)
+
+ imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle))
+ updateConstraints(state.currentStep, R.layout.lp_onboarding_create_wallet)
+ }
+
+ private fun setupTopUpWalletState(state: OnboardingNoteState) {
+ btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto)
+ btn_main_action.setOnClickListener {
+ store.dispatch(OnboardingNoteAction.TopUp)
+ }
+
+ btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address)
+ btn_alternative_action.setOnClickListener {
+ store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog)
+ }
+
+ tv_header.setText(R.string.onboarding_top_up_header)
+ tv_body.setText(R.string.onboarding_top_up_body)
+
+ btnRefreshBalanceWidget.changeState(state.walletBalance.state)
+ if (btnRefreshBalanceWidget.isShowing != true) {
+ postUi(300) {
+ btnRefreshBalanceWidget.mainView.setOnClickListener {
+ store.dispatch(OnboardingNoteAction.Balance.Update)
+ }
+ }
+ }
+ imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
+ updateConstraints(state.currentStep, R.layout.lp_onboarding_topup_wallet)
+ }
+
+ private fun setupDoneState(state: OnboardingNoteState) {
+ btn_main_action.setText(R.string.onboarding_done_button_continue)
+ btn_main_action.setOnClickListener {
+ showConfetti(false)
+ store.dispatch(OnboardingNoteAction.Done)
+ }
+
+ btn_alternative_action.isVisible = false
+ btn_alternative_action.setText("")
+ btn_alternative_action.setOnClickListener { }
+ btnRefreshBalanceWidget.mainView.setOnClickListener(null)
+
+ tv_header.setText(R.string.onboarding_done_header)
+ tv_body.setText(R.string.onboarding_done_body)
+
+ imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
+ updateConstraints(state.currentStep, R.layout.lp_onboarding_done_activation)
+ }
+
+ private fun updateConstraints(currentStep: OnboardingNoteStep, @LayoutRes layoutId: Int) {
+ if (this.currentStep == currentStep) return
+
+ val constraintSet = ConstraintSet()
+ constraintSet.clone(requireContext(), layoutId)
+ constraintSet.applyTo(onboarding_main_container)
+ val transition = InternalNoteLayoutTransition()
+ transition.interpolator = OvershootInterpolator()
+ TransitionManager.beginDelayedTransition(onboarding_main_container, transition)
+ }
+}
+
+fun ImageView.swapToBitmapDrawable(bitmap: Bitmap?, duration: Long = 200) {
+ if (drawable is BitmapDrawable && (drawable as BitmapDrawable).bitmap == bitmap) return
+
+ fadeOut(duration) {
+ setImageBitmap(bitmap)
+ fadeIn(duration)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt
new file mode 100644
index 0000000000..798f8f4136
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt
@@ -0,0 +1,36 @@
+package com.tangem.tap.features.onboarding.products.note.redux
+
+import com.tangem.blockchain.common.WalletManager
+import com.tangem.tap.features.onboarding.OnboardingWalletBalance
+import com.tangem.tap.features.wallet.redux.Artwork
+import org.rekotlin.Action
+
+/**
+[REDACTED_AUTHOR]
+ */
+sealed class OnboardingNoteAction : Action {
+ // from user, ui
+ data class SetResources(val resources: AndroidResources) : OnboardingNoteAction()
+ object LoadCardArtwork : OnboardingNoteAction()
+ object CreateWallet : OnboardingNoteAction()
+ object TopUp : OnboardingNoteAction()
+ object ShowAddressInfoDialog : OnboardingNoteAction()
+
+ // from redux
+ class SetArtworkUrl(val artwork: Artwork) : OnboardingNoteAction()
+ data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction()
+ object DetermineStepOfScreen : OnboardingNoteAction()
+ object Done : OnboardingNoteAction()
+
+ data class SetStepOfScreen(val step: OnboardingNoteStep) : OnboardingNoteAction()
+
+ sealed class Balance {
+ object Update : OnboardingNoteAction()
+ data class Set(val balance: OnboardingWalletBalance) : OnboardingNoteAction()
+ }
+
+ sealed class Confetti {
+ object Show : OnboardingNoteAction()
+ object Hide : OnboardingNoteAction()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt
new file mode 100644
index 0000000000..0a925dc7ee
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt
@@ -0,0 +1,151 @@
+package com.tangem.tap.features.onboarding.products.note.redux
+
+import com.tangem.common.CompletionResult
+import com.tangem.common.extensions.guard
+import com.tangem.tap.common.extensions.*
+import com.tangem.tap.common.postUi
+import com.tangem.tap.common.redux.AppDialog
+import com.tangem.tap.common.redux.AppState
+import com.tangem.tap.common.redux.global.GlobalAction
+import com.tangem.tap.common.redux.navigation.AppScreen
+import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.domain.TapError
+import com.tangem.tap.domain.extensions.hasWallets
+import com.tangem.tap.domain.extensions.makePrimaryWalletManager
+import com.tangem.tap.features.wallet.redux.ProgressState
+import com.tangem.tap.scope
+import com.tangem.tap.store
+import com.tangem.tap.tangemSdkManager
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import org.rekotlin.Action
+import org.rekotlin.DispatchFunction
+import org.rekotlin.Middleware
+
+class OnboardingNoteMiddleware {
+ companion object {
+ val handler = onboardingNoteMiddleware
+ }
+}
+
+private val onboardingNoteMiddleware: Middleware = { dispatch, state ->
+ { next ->
+ { action ->
+ handleNoteAction(action, dispatch)
+ next(action)
+ }
+ }
+}
+
+private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
+ if (action !is OnboardingNoteAction) return
+ val globalState = store.state.globalState
+ val onboardingManager = globalState.onboardingManager ?: return
+
+ val scanResponse = onboardingManager.scanResponse
+ val card = onboardingManager.scanResponse.card
+ val noteState = store.state.onboardingNoteState
+
+ when (action) {
+ is OnboardingNoteAction.LoadCardArtwork -> {
+ scope.launch {
+ val artwork = onboardingManager.loadArtwork()
+ withMainContext { store.dispatch(OnboardingNoteAction.SetArtworkUrl(artwork)) }
+ }
+ }
+ is OnboardingNoteAction.DetermineStepOfScreen -> {
+ val step = when {
+ !card.hasWallets() -> OnboardingNoteStep.CreateWallet
+ noteState.walletBalance.balanceIsToppedUp() -> OnboardingNoteStep.Done
+ else -> OnboardingNoteStep.TopUpWallet
+ }
+ store.dispatch((OnboardingNoteAction.SetStepOfScreen(step)))
+ }
+ is OnboardingNoteAction.SetStepOfScreen -> {
+ when (action.step) {
+ OnboardingNoteStep.TopUpWallet -> {
+ store.dispatch(OnboardingNoteAction.Balance.Update)
+ }
+ OnboardingNoteStep.Done -> {
+ onboardingManager.activationFinished(card.cardId)
+ postUi(500) { store.dispatch(OnboardingNoteAction.Confetti.Show) }
+ }
+ }
+ }
+ is OnboardingNoteAction.CreateWallet -> {
+ scope.launch {
+ val result = tangemSdkManager.createProductWallet(scanResponse)
+ withMainContext {
+ when (result) {
+ is CompletionResult.Success -> {
+ val updatedResponse = scanResponse.copy(card = result.data)
+ onboardingManager.scanResponse = updatedResponse
+ onboardingManager.activationStarted(updatedResponse.card.cardId)
+ store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.TopUpWallet))
+ }
+ is CompletionResult.Failure -> {
+// do nothing
+ }
+ }
+ }
+ }
+ }
+ is OnboardingNoteAction.Balance.Update -> {
+ val walletManager = if (noteState.walletManager != null) {
+ noteState.walletManager
+ } else {
+ val wmFactory = globalState.tapWalletManager.walletManagerFactory
+ val walletManager = wmFactory.makePrimaryWalletManager(scanResponse).guard {
+ val message = "Loading cancelled. Cause: wallet manager didn't created"
+ val customError = TapError.CustomError(message)
+ store.dispatchErrorNotification(customError)
+ return
+ }
+ dispatch(OnboardingNoteAction.SetWalletManager(walletManager))
+ walletManager
+ }
+
+ val isLoadedBefore = noteState.walletBalance.state != ProgressState.Loading
+ val balanceIsLoading = noteState.walletBalance.copy(
+ state = ProgressState.Loading,
+ error = null,
+ criticalError = null
+ )
+ store.dispatch(OnboardingNoteAction.Balance.Set(balanceIsLoading))
+
+ scope.launch {
+ val loadedBalance = onboardingManager.updateBalance(walletManager)
+ loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) }
+ delay(if (isLoadedBefore) 0 else 300)
+ withMainContext { store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) }
+ }
+
+ }
+ is OnboardingNoteAction.Balance.Set -> {
+ if (action.balance.balanceIsToppedUp()) {
+ store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.Done))
+ }
+ }
+ is OnboardingNoteAction.ShowAddressInfoDialog -> {
+ val addressData = noteState.walletManager?.getAddressData() ?: return
+ val addressWasCopied = noteState.resources.strings.addressWasCopied
+ val appDialog = AppDialog.AddressInfoDialog(
+ addressData,
+ onCopyAddress = { store.dispatchToastNotification(addressWasCopied) },
+ onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) }
+ )
+ store.dispatchDialogShow(appDialog)
+ }
+ is OnboardingNoteAction.TopUp -> {
+ val topUpUrl = noteState.walletManager?.getToUpUrl() ?: return
+ store.dispatchOpenUrl(topUpUrl)
+ }
+ OnboardingNoteAction.Done -> {
+ store.dispatch(GlobalAction.Onboarding.Stop)
+ scope.launch {
+ globalState.tapWalletManager.onCardScanned(scanResponse)
+ store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt
new file mode 100644
index 0000000000..03415eec84
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt
@@ -0,0 +1,46 @@
+package com.tangem.tap.features.onboarding.products.note.redux
+
+import com.tangem.tap.common.redux.AppState
+import com.tangem.tap.common.redux.global.GlobalAction
+import org.rekotlin.Action
+
+class OnboardingNoteReducer {
+ companion object {
+ fun reduce(action: Action, state: AppState): OnboardingNoteState = internalReduce(action, state)
+ }
+}
+
+private fun internalReduce(action: Action, appState: AppState): OnboardingNoteState {
+ var state = appState.onboardingNoteState
+
+ when (action) {
+ is GlobalAction.Onboarding.Start -> {
+ state = OnboardingNoteState()
+ }
+ is OnboardingNoteAction.SetArtworkUrl -> {
+ state = state.copy(cardArtwork = action.artwork)
+ }
+ is OnboardingNoteAction.SetWalletManager -> {
+ state = state.copy(walletManager = action.walletManager)
+ }
+ is OnboardingNoteAction.SetResources -> {
+ state = state.copy(resources = action.resources)
+ }
+ is OnboardingNoteAction.Balance.Set -> {
+ state = state.copy(walletBalance = action.balance)
+ }
+ is OnboardingNoteAction.SetStepOfScreen -> {
+ if (action.step != state.currentStep && state.steps.contains(action.step)) {
+ state = state.copy(currentStep = action.step)
+ }
+ }
+ is OnboardingNoteAction.Confetti.Show -> {
+ state = state.copy(showConfetti = true)
+ }
+ is OnboardingNoteAction.Confetti.Hide -> {
+ state = state.copy(showConfetti = false)
+ }
+ }
+
+ return state
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt
new file mode 100644
index 0000000000..d4f1c2bcb7
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt
@@ -0,0 +1,36 @@
+package com.tangem.tap.features.onboarding.products.note.redux
+
+import com.tangem.blockchain.common.WalletManager
+import com.tangem.tap.features.onboarding.OnboardingWalletBalance
+import com.tangem.tap.features.wallet.redux.Artwork
+import org.rekotlin.StateType
+
+/**
+[REDACTED_AUTHOR]
+ */
+data class OnboardingNoteState(
+ val walletManager: WalletManager? = null,
+ val resources: AndroidResources = AndroidResources(),
+ // UI
+ val cardArtwork: Artwork? = null,
+ val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(),
+ val currentStep: OnboardingNoteStep = OnboardingNoteStep.None,
+ val steps: List = OnboardingNoteStep.values().toList(),
+ val showConfetti: Boolean = false,
+) : StateType {
+
+ val progress: Int
+ get() = steps.indexOf(currentStep)
+}
+
+enum class OnboardingNoteStep {
+ None, CreateWallet, TopUpWallet, Done
+}
+
+data class AndroidResources(
+ val strings: OnboardingStringResources = OnboardingStringResources()
+)
+
+data class OnboardingStringResources(
+ val addressWasCopied: Int = -1
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt
new file mode 100644
index 0000000000..ebdbe7d4c1
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt
@@ -0,0 +1,102 @@
+package com.tangem.tap.features.onboarding.products.otherCards
+
+import android.os.Bundle
+import android.view.View
+import android.view.animation.OvershootInterpolator
+import androidx.annotation.LayoutRes
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.view.isVisible
+import androidx.transition.TransitionManager
+import com.tangem.tap.common.extensions.getDrawableCompat
+import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
+import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
+import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable
+import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction
+import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState
+import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep
+import com.tangem.tap.store
+import com.tangem.wallet.R
+import kotlinx.android.synthetic.main.fragment_onboarding_main.*
+import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.*
+import kotlinx.android.synthetic.main.layout_onboarding_note.*
+import kotlinx.android.synthetic.main.view_onboarding_progress.*
+
+/**
+[REDACTED_AUTHOR]
+ */
+class OnboardingOtherCardsFragment : BaseOnboardingFragment() {
+
+ override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ toolbar.setTitle(R.string.onboarding_title)
+ store.dispatch(OnboardingOtherCardsAction.LoadCardArtwork)
+ store.dispatch(OnboardingOtherCardsAction.DetermineStepOfScreen)
+ }
+
+ override fun subscribeToStore() {
+ store.subscribe(this) { state ->
+ state.skipRepeats { oldState, newState ->
+ oldState.onboardingOtherCardsState == newState.onboardingOtherCardsState
+ }.select { it.onboardingOtherCardsState }
+ }
+ storeSubscribersList.add(this)
+ }
+
+ override fun newState(state: OnboardingOtherCardsState) {
+ if (activity == null) return
+ if (state.currentStep == OnboardingOtherCardsStep.None) return
+
+ state.cardArtwork?.let { imv_front_card.swapToBitmapDrawable(it.artwork) }
+ pb_state.max = state.steps.size - 1
+ pb_state.progress = state.progress
+
+ when (state.currentStep) {
+ OnboardingOtherCardsStep.CreateWallet -> setupCreateWalletState(state)
+ OnboardingOtherCardsStep.Done -> setupDoneState(state)
+ }
+ showConfetti(state.showConfetti)
+ }
+
+ private fun setupCreateWalletState(state: OnboardingOtherCardsState) {
+ btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet)
+ btn_main_action.setOnClickListener { store.dispatch(OnboardingOtherCardsAction.CreateWallet) }
+ btn_alternative_action.setText(R.string.onboarding_button_what_does_it_mean)
+ btn_alternative_action.setOnClickListener { }
+
+ tv_header.setText(R.string.onboarding_create_wallet_header)
+ tv_body.setText(R.string.onboarding_create_wallet_body)
+
+ imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle))
+ updateConstraints(R.layout.lp_onboarding_create_wallet)
+ }
+
+ private fun setupDoneState(state: OnboardingOtherCardsState) {
+ btn_main_action.setText(R.string.onboarding_done_button_continue)
+ btn_main_action.setOnClickListener {
+ showConfetti(false)
+ store.dispatch(OnboardingOtherCardsAction.Done)
+ }
+
+ btn_alternative_action.isVisible = false
+ btn_alternative_action.setText("")
+ btn_alternative_action.setOnClickListener { }
+
+ tv_header.setText(R.string.onboarding_done_header)
+ tv_body.setText(R.string.onboarding_done_body)
+
+ imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
+ updateConstraints(R.layout.lp_onboarding_done)
+ }
+
+ private fun updateConstraints(@LayoutRes layoutId: Int) {
+ val constraintSet = ConstraintSet()
+ constraintSet.clone(requireContext(), layoutId)
+ constraintSet.applyTo(onboarding_main_container)
+ val transition = InternalNoteLayoutTransition()
+ transition.interpolator = OvershootInterpolator()
+ TransitionManager.beginDelayedTransition(onboarding_main_container, transition)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt
new file mode 100644
index 0000000000..44bdec53c8
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt
@@ -0,0 +1,25 @@
+package com.tangem.tap.features.onboarding.products.otherCards.redux
+
+import com.tangem.tap.features.wallet.redux.Artwork
+import org.rekotlin.Action
+
+/**
+[REDACTED_AUTHOR]
+ */
+sealed class OnboardingOtherCardsAction : Action {
+ // from user, ui
+ object LoadCardArtwork : OnboardingOtherCardsAction()
+ object CreateWallet : OnboardingOtherCardsAction()
+
+ // from redux
+ class SetArtworkUrl(val artwork: Artwork) : OnboardingOtherCardsAction()
+ object DetermineStepOfScreen : OnboardingOtherCardsAction()
+ object Done : OnboardingOtherCardsAction()
+
+ data class SetStepOfScreen(val step: OnboardingOtherCardsStep) : OnboardingOtherCardsAction()
+
+ sealed class Confetti {
+ object Show : OnboardingOtherCardsAction()
+ object Hide : OnboardingOtherCardsAction()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt
new file mode 100644
index 0000000000..e775176f10
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt
@@ -0,0 +1,91 @@
+package com.tangem.tap.features.onboarding.products.otherCards.redux
+
+import com.tangem.common.CompletionResult
+import com.tangem.tap.common.extensions.withMainContext
+import com.tangem.tap.common.postUi
+import com.tangem.tap.common.redux.AppState
+import com.tangem.tap.common.redux.global.GlobalAction
+import com.tangem.tap.common.redux.navigation.AppScreen
+import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE
+import com.tangem.tap.domain.extensions.hasWallets
+import com.tangem.tap.scope
+import com.tangem.tap.store
+import com.tangem.tap.tangemSdkManager
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import org.rekotlin.Action
+import org.rekotlin.DispatchFunction
+import org.rekotlin.Middleware
+
+class OnboardingOtherCardsMiddleware {
+ companion object {
+ val handler = onboardingOtherCardsMiddleware
+ }
+}
+
+private val onboardingOtherCardsMiddleware: Middleware = { dispatch, state ->
+ { next ->
+ { action ->
+ handleOtherCardsAction(action, dispatch)
+ next(action)
+ }
+ }
+}
+
+private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) {
+ if (action !is OnboardingOtherCardsAction) return
+ val globalState = store.state.globalState
+ val onboardingManager = globalState.onboardingManager ?: return
+
+ val card = onboardingManager.scanResponse.card
+
+ when (action) {
+ is OnboardingOtherCardsAction.LoadCardArtwork -> {
+ scope.launch {
+ val artwork = onboardingManager.loadArtwork()
+ withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtworkUrl(artwork)) }
+ }
+ }
+ is OnboardingOtherCardsAction.DetermineStepOfScreen -> {
+ val step = when {
+ !card.hasWallets() -> OnboardingOtherCardsStep.CreateWallet
+ else -> OnboardingOtherCardsStep.Done
+ }
+ store.dispatch((OnboardingOtherCardsAction.SetStepOfScreen(step)))
+ }
+ is OnboardingOtherCardsAction.SetStepOfScreen -> {
+ if (action.step == OnboardingOtherCardsStep.Done) {
+ onboardingManager.activationFinished(card.cardId)
+ postUi(500) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) }
+ }
+ }
+ is OnboardingOtherCardsAction.CreateWallet -> {
+ scope.launch {
+ val result = tangemSdkManager.createProductWallet(onboardingManager.scanResponse)
+ withMainContext {
+ when (result) {
+ is CompletionResult.Success -> {
+ val updatedResponse = onboardingManager.scanResponse.copy(card = result.data)
+ onboardingManager.scanResponse = updatedResponse
+ onboardingManager.activationStarted(updatedResponse.card.cardId)
+
+ delay(DELAY_SDK_DIALOG_CLOSE)
+ store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done))
+ }
+ is CompletionResult.Failure -> {
+// do nothing
+ }
+ }
+ }
+ }
+ }
+ OnboardingOtherCardsAction.Done -> {
+ store.dispatch(GlobalAction.Onboarding.Stop)
+ scope.launch {
+ globalState.tapWalletManager.onCardScanned(onboardingManager.scanResponse)
+ store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt
new file mode 100644
index 0000000000..a6313b9199
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt
@@ -0,0 +1,37 @@
+package com.tangem.tap.features.onboarding.products.otherCards.redux
+
+import com.tangem.tap.common.redux.AppState
+import com.tangem.tap.common.redux.global.GlobalAction
+import org.rekotlin.Action
+
+class OnboardingOtherCardsReducer {
+ companion object {
+ fun reduce(action: Action, state: AppState): OnboardingOtherCardsState = internalReduce(action, state)
+ }
+}
+
+private fun internalReduce(action: Action, appState: AppState): OnboardingOtherCardsState {
+ var state = appState.onboardingOtherCardsState
+
+ when (action) {
+ is GlobalAction.Onboarding.Start -> {
+ state = OnboardingOtherCardsState()
+ }
+ is OnboardingOtherCardsAction.SetArtworkUrl -> {
+ state = state.copy(cardArtwork = action.artwork)
+ }
+ is OnboardingOtherCardsAction.SetStepOfScreen -> {
+ if (action.step != state.currentStep && state.steps.contains(action.step)) {
+ state = state.copy(currentStep = action.step)
+ }
+ }
+ is OnboardingOtherCardsAction.Confetti.Show -> {
+ state = state.copy(showConfetti = true)
+ }
+ is OnboardingOtherCardsAction.Confetti.Hide -> {
+ state = state.copy(showConfetti = false)
+ }
+ }
+
+ return state
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt
new file mode 100644
index 0000000000..b306101506
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt
@@ -0,0 +1,23 @@
+package com.tangem.tap.features.onboarding.products.otherCards.redux
+
+import com.tangem.tap.features.wallet.redux.Artwork
+import org.rekotlin.StateType
+
+/**
+[REDACTED_AUTHOR]
+ */
+data class OnboardingOtherCardsState(
+ // UI
+ val cardArtwork: Artwork? = null,
+ val showConfetti: Boolean = false,
+ val currentStep: OnboardingOtherCardsStep = OnboardingOtherCardsStep.None,
+ val steps: List = OnboardingOtherCardsStep.values().toList(),
+) : StateType {
+
+ val progress: Int
+ get() = steps.indexOf(currentStep)
+}
+
+enum class OnboardingOtherCardsStep {
+ None, CreateWallet, Done
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt
new file mode 100644
index 0000000000..d0463040bd
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt
@@ -0,0 +1,42 @@
+package com.tangem.tap.features.onboarding.products.wallet
+
+import android.os.Bundle
+import android.view.View
+import androidx.transition.TransitionInflater
+import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
+import com.tangem.tap.features.send.BaseStoreFragment
+import com.tangem.tap.store
+import com.tangem.wallet.R
+import org.rekotlin.StoreSubscriber
+
+/**
+[REDACTED_AUTHOR]
+ */
+class OnboardingWalletFragment : BaseStoreFragment(R.layout.fragment_onboarding_wallet), StoreSubscriber {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val inflater = TransitionInflater.from(requireContext())
+ enterTransition = inflater.inflateTransition(R.transition.fade)
+ exitTransition = inflater.inflateTransition(R.transition.fade)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ }
+
+ override fun subscribeToStore() {
+ store.subscribe(this) { state ->
+ state.skipRepeats { oldState, newState ->
+ oldState.onboardingWalletState == newState.onboardingWalletState
+ }.select { it.onboardingWalletState }
+ }
+ storeSubscribersList.add(this)
+ }
+
+ override fun newState(state: OnboardingWalletState) {
+ if (activity == null) return
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt
new file mode 100644
index 0000000000..5cfd213d57
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt
@@ -0,0 +1,19 @@
+package com.tangem.tap.features.onboarding.products.wallet.redux
+
+import org.rekotlin.Action
+
+/**
+[REDACTED_AUTHOR]
+ */
+sealed class OnboardingWalletAction : Action {
+ object Init : OnboardingWalletAction()
+ object CreateWallet : OnboardingWalletAction()
+ object TopUp : OnboardingWalletAction()
+ object Done : OnboardingWalletAction()
+
+ object ProceedBackup : OnboardingWalletAction()
+
+ sealed class Backup : Action {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
new file mode 100644
index 0000000000..c442090de4
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
@@ -0,0 +1,18 @@
+package com.tangem.tap.features.onboarding.products.wallet.redux
+
+import com.tangem.tap.common.redux.AppState
+import org.rekotlin.Middleware
+
+class OnboardingWalletMiddleware {
+ companion object {
+ val handler = onboardingWalletMiddleware
+ }
+}
+
+private val onboardingWalletMiddleware: Middleware = { dispatch, state ->
+ { next ->
+ { action ->
+ next(action)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt
new file mode 100644
index 0000000000..427f44048e
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt
@@ -0,0 +1,20 @@
+package com.tangem.tap.features.onboarding.products.wallet.redux
+
+import com.tangem.tap.common.redux.AppState
+import org.rekotlin.Action
+
+class OnboardingWalletReducer {
+ companion object {
+ fun reduce(action: Action, state: AppState): OnboardingWalletState = internalReduce(action, state)
+ }
+}
+
+private fun internalReduce(action: Action, appState: AppState): OnboardingWalletState {
+ var state = appState.onboardingWalletState
+
+ when (action) {
+
+ }
+
+ return state
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt
new file mode 100644
index 0000000000..5207a85b3d
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt
@@ -0,0 +1,14 @@
+package com.tangem.tap.features.onboarding.products.wallet.redux
+
+import org.rekotlin.StateType
+
+/**
+[REDACTED_AUTHOR]
+ */
+data class OnboardingWalletState(
+ val any: String? = null
+) : StateType
+
+enum class OnboardingWalletStep {
+ None, CreateWallet, TopUpWallet, Backup, Done
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt
index fb46ed08da..bb3046db5e 100644
--- a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt
@@ -4,11 +4,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.google.android.material.snackbar.Snackbar
-import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_wallet.*
@@ -26,11 +24,10 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
- override fun handleOnBackPressed() {
- store.dispatch(NavigationAction.PopBackTo())
- }
- })
+ configureTransitions()
+ }
+
+ protected open fun configureTransitions() {
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(R.transition.slide_right)
exitTransition = inflater.inflateTransition(R.transition.fade)
@@ -43,7 +40,7 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- toolbar.setNavigationOnClickListener { store.dispatch(NavigationAction.PopBackTo()) }
+ toolbar?.setNavigationOnClickListener { activity?.onBackPressed() }
}
override fun onStart() {
@@ -54,6 +51,7 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) {
override fun onStop() {
storeSubscribersList.forEach { store.unsubscribe(it) }
+ storeSubscribersList.clear()
super.onStop()
}
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt
index 08c3b35a09..b0b9263034 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt
@@ -5,13 +5,13 @@ import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager
import com.tangem.tap.common.redux.ErrorAction
+import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.ToastNotificationAction
-import com.tangem.tap.common.redux.global.StateDialog
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
+import com.tangem.tap.features.send.redux.states.ButtonState
import com.tangem.tap.features.send.redux.states.FeeType
import com.tangem.tap.features.send.redux.states.MainCurrencyType
-import com.tangem.tap.features.send.redux.states.SendButtonState
import com.tangem.wallet.R
import org.rekotlin.Action
import java.math.BigDecimal
@@ -132,11 +132,12 @@ sealed class ReceiptAction : SendScreenAction {
sealed class SendActionUi : SendScreenActionUi {
data class SendAmountToRecipient(val messageForSigner: Message) : SendScreenActionUi
+ object CheckIfTransactionDataWasProvided : SendScreenActionUi
}
sealed class SendAction : SendScreenAction {
- data class ChangeSendButtonState(val state: SendButtonState) : SendAction()
+ data class ChangeSendButtonState(val state: ButtonState) : SendAction()
object SendSuccess : SendAction(), ToastNotificationAction {
override val messageResource: Int = R.string.send_transaction_success
}
@@ -158,4 +159,11 @@ sealed class SendAction : SendScreenAction {
object Update : SendAction()
data class Set(val warningList: List) : SendAction()
}
+
+ data class SendSpecificTransaction(
+ val sendAmount: String,
+ val destinationAddress: String,
+ val transactionId: String
+ ) : SendAction()
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt
index 3da9747dd3..33a7313b5d 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt
@@ -2,7 +2,7 @@ package com.tangem.tap.features.send.redux.middlewares
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Wallet
-import com.tangem.commands.common.network.Result
+import com.tangem.common.services.Result
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.PayIdManager
import com.tangem.tap.domain.isPayIdSupported
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt
index 7dcececcda..26d2f8dd27 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt
@@ -5,9 +5,11 @@ import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
import com.tangem.blockchain.common.*
import com.tangem.blockchain.extensions.SimpleResult
-import com.tangem.commands.common.card.Card
+import com.tangem.common.card.Card
+import com.tangem.common.core.TangemSdkError
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
+import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
@@ -19,7 +21,9 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.extensions.minimalAmount
import com.tangem.tap.features.send.redux.*
import com.tangem.tap.features.send.redux.FeeAction.RequestFee
-import com.tangem.tap.features.send.redux.states.SendButtonState
+import com.tangem.tap.features.send.redux.states.ButtonState
+import com.tangem.tap.features.send.redux.states.ExternalTransactionData
+import com.tangem.tap.features.send.redux.states.MainCurrencyType
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.scope
@@ -37,29 +41,43 @@ import java.util.*
/**
[REDACTED_AUTHOR]
*/
-val sendMiddleware: Middleware = { dispatch, appState ->
- { nextDispatch ->
- { action ->
- when (action) {
- is AddressPayIdActionUi -> AddressPayIdMiddleware().handle(action, appState(), dispatch)
- is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch)
- is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch)
- is SendActionUi.SendAmountToRecipient ->
- verifyAndSendTransaction(action, appState(), dispatch)
- is PrepareSendScreen -> setIfSendingToPayIdEnabled(appState(), dispatch)
- is SendAction.Warnings.Update -> updateWarnings(dispatch)
+class SendMiddleware {
+ val sendMiddleware: Middleware = { dispatch, appState ->
+ { nextDispatch ->
+ { action ->
+ when (action) {
+ is AddressPayIdActionUi -> AddressPayIdMiddleware().handle(action, appState(), dispatch)
+ is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch)
+ is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch)
+ is SendActionUi.SendAmountToRecipient ->
+ verifyAndSendTransaction(action, appState(), dispatch)
+ is PrepareSendScreen -> setIfSendingToPayIdEnabled(appState(), dispatch)
+ is SendAction.Warnings.Update -> updateWarnings(dispatch)
+ is SendActionUi.CheckIfTransactionDataWasProvided -> {
+ val transactionData = appState()?.sendState?.externalTransactionData
+ if (transactionData != null) {
+ store.dispatchOnMain(AddressPayIdVerifyAction.AddressVerification.SetWalletAddress(
+ transactionData.destinationAddress, false
+ ))
+ store.dispatchOnMain(AmountActionUi.SetMainCurrency(MainCurrencyType.CRYPTO))
+ store.dispatchOnMain(AmountActionUi.HandleUserInput(transactionData.amount))
+ store.dispatchOnMain(AmountAction.SetAmount(transactionData.amount.toBigDecimal(),
+ false))
+ }
+ }
+ }
+ nextDispatch(action)
}
- nextDispatch(action)
}
}
-}
+}
private fun verifyAndSendTransaction(
action: SendActionUi.SendAmountToRecipient, appState: AppState?, dispatch: (Action) -> Unit,
) {
val sendState = appState?.sendState ?: return
val walletManager = sendState.walletManager ?: return
- val card = appState.globalState.scanNoteResponse?.card ?: return
+ val card = appState.globalState.scanResponse?.card ?: return
val destinationAddress = sendState.addressPayIdState.destinationWalletAddress ?: return
val typedAmount = sendState.amountState.amountToExtract ?: return
val feeAmount = sendState.feeState.currentFee ?: return
@@ -75,8 +93,11 @@ private fun verifyAndSendTransaction(
dispatch(AmountAction.SetAmount(typedAmount.value!!.minus(reduceAmount), false))
dispatch(AmountActionUi.CheckAmountToSend)
}, sendAllCallback = {
- sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress,
- sendState.transactionExtrasState, card, dispatch)
+ sendTransaction(
+ action, walletManager, amountToSend, feeAmount, destinationAddress,
+ sendState.transactionExtrasState, card, sendState.externalTransactionData,
+ dispatch
+ )
}, reduceAmount))
}
transactionErrors.isNotEmpty() -> {
@@ -84,7 +105,7 @@ private fun verifyAndSendTransaction(
}
else -> {
sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress,
- sendState.transactionExtrasState, card, dispatch)
+ sendState.transactionExtrasState, card, sendState.externalTransactionData, dispatch)
}
}
}
@@ -97,9 +118,10 @@ private fun sendTransaction(
destinationAddress: String,
transactionExtras: TransactionExtrasState,
card: Card,
+ externalTransactionData: ExternalTransactionData?,
dispatch: (Action) -> Unit,
) {
- dispatch(SendAction.ChangeSendButtonState(SendButtonState.PROGRESS))
+ dispatch(SendAction.ChangeSendButtonState(ButtonState.PROGRESS))
var txData = walletManager.createTransaction(amountToSend, feeAmount, destinationAddress)
transactionExtras.xlmMemo?.memo?.let { txData = txData.copy(extras = StellarTransactionExtras(it)) }
@@ -116,9 +138,9 @@ private fun sendTransaction(
) { signResponse ->
store.dispatch(
GlobalAction.UpdateWalletSignedHashes(
- walletSignedHashes = signResponse.walletSignedHashes,
- remainingSignatures = signResponse.walletRemainingSignatures,
- walletPublicKey = walletManager.wallet.publicKey
+ walletSignedHashes = signResponse.totalSignedHashes,
+ walletPublicKey = walletManager.wallet.publicKey,
+ remainingSignatures = signResponse.remainingSignatures
)
)
}
@@ -127,9 +149,18 @@ private fun sendTransaction(
when (result) {
is SimpleResult.Success -> {
tangemSdk.config.linkedTerminal = isLinkedTerminal
- FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.TRANSACTION_IS_SENT, card)
+ FirebaseAnalyticsHandler.triggerEvent(
+ event = AnalyticsEvent.TRANSACTION_IS_SENT,
+ card = card,
+ blockchain = walletManager.wallet.blockchain.currency
+ )
dispatch(SendAction.SendSuccess)
- dispatch(NavigationAction.PopBackTo())
+
+ if (externalTransactionData != null) {
+ dispatch(WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId))
+ } else {
+ dispatch(NavigationAction.PopBackTo())
+ }
scope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) {
dispatch(WalletAction.UpdateWallet(walletManager.wallet.blockchain))
@@ -178,6 +209,16 @@ private fun sendTransaction(
dispatch(SendAction.SendError(TapError.XmlError.AssetAccountNotCreated))
}
else -> {
+ (result.error as? TangemSdkError)?.let { error ->
+ FirebaseAnalyticsHandler.logCardSdkError(
+ error,
+ FirebaseAnalyticsHandler.ActionToLog.SendTransaction,
+ mapOf(
+ FirebaseAnalyticsHandler.AnalyticsParam.BLOCKCHAIN
+ to walletManager.wallet.blockchain.currency),
+ card = card,
+ )
+ }
Timber.e(throwable)
FirebaseCrashlytics.getInstance().recordException(throwable)
dispatch(SendAction.SendError(TapError.CustomError(message)))
@@ -195,7 +236,7 @@ private fun sendTransaction(
}
}
}
- dispatch(SendAction.ChangeSendButtonState(SendButtonState.ENABLED))
+ dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED))
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt
index 8cad24d39c..673f19cb66 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt
@@ -2,7 +2,9 @@ package com.tangem.tap.features.send.redux.reducers
import com.tangem.blockchain.common.AmountType
import com.tangem.tap.common.CurrencyConverter
+import com.tangem.tap.common.entities.IndeterminateProgressButton
import com.tangem.tap.features.send.redux.*
+import com.tangem.tap.features.send.redux.states.ExternalTransactionData
import com.tangem.tap.features.send.redux.states.IdStateHolder
import com.tangem.tap.features.send.redux.states.SendState
import org.rekotlin.Action
@@ -40,16 +42,38 @@ class SendScreenReducer {
private class SendReducer : SendInternalReducer {
override fun handle(action: SendScreenAction, sendState: SendState): SendState {
val result = when (action) {
- is SendAction.ChangeSendButtonState -> sendState.copy(sendButtonState = action.state)
+ is SendAction.ChangeSendButtonState -> {
+ sendState.copy(sendButtonState = IndeterminateProgressButton(action.state))
+ }
is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action)
is SendAction.Dialog.SendTransactionFails -> sendState.copy(dialog = action)
is SendAction.Dialog.Hide -> sendState.copy(dialog = null)
is SendAction.Warnings.Set -> sendState.copy(sendWarningsList = action.warningList)
+ is SendAction.SendSpecificTransaction ->
+ handleSendSpecificTransactionAction(action, sendState)
else -> return sendState
}
return updateLastState(result, result)
}
+
+ private fun handleSendSpecificTransactionAction(
+ action: SendAction.SendSpecificTransaction, state: SendState
+ ): SendState {
+ return state.copy(
+ externalTransactionData =
+ ExternalTransactionData(action.sendAmount, action.destinationAddress, action.transactionId),
+ feeState = state.feeState.copy(
+ includeFeeSwitcherIsEnabled = false
+ ),
+ amountState = state.amountState.copy(
+ inputIsEnabled = false
+ ),
+ addressPayIdState = state.addressPayIdState.copy(
+ inputIsEnabled = false
+ ),
+ )
+ }
}
private class EmptyReducer : SendInternalReducer {
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt
index d17531a84a..4f2e4e1a6d 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt
@@ -12,7 +12,8 @@ data class AddressPayIdState(
val error: AddressPayIdVerifyAction.Error? = null,
val truncateHandler: ((String) -> String)? = null,
val sendingToPayIdEnabled: Boolean = false,
- val pasteIsEnabled: Boolean = false
+ val pasteIsEnabled: Boolean = false,
+ val inputIsEnabled: Boolean = true
) : SendScreenState {
override val stateId: StateId = StateId.ADDRESS_PAY_ID
diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt
index 41d2fd79c5..ed98986ae3 100644
--- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt
@@ -5,8 +5,9 @@ import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.extensions.isZero
import com.tangem.tap.common.CurrencyConverter
+import com.tangem.tap.common.entities.IndeterminateProgressButton
import com.tangem.tap.common.entities.TapCurrency
-import com.tangem.tap.common.redux.global.StateDialog
+import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.text.DecimalDigitsInputFilter
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
@@ -39,8 +40,9 @@ data class SendState(
val feeState: FeeState = FeeState(),
val receiptState: ReceiptState = ReceiptState(),
val sendWarningsList: List = listOf(),
- val sendButtonState: SendButtonState = SendButtonState.DISABLED,
- val dialog: StateDialog? = null
+ val sendButtonState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.DISABLED),
+ val dialog: StateDialog? = null,
+ val externalTransactionData: ExternalTransactionData? = null
) : SendScreenState {
override val stateId: StateId = StateId.SEND_SCREEN
@@ -86,7 +88,7 @@ data class SendState(
}
}
- fun getButtonState(): SendButtonState = if (isReadyToSend()) SendButtonState.ENABLED else SendButtonState.DISABLED
+ fun getButtonState(): ButtonState = if (isReadyToSend()) ButtonState.ENABLED else ButtonState.DISABLED
fun getTotalAmountToSend(value: BigDecimal = amountState.amountToSendCrypto): BigDecimal {
val needToExtractFee = amountState.isCoinAmount() && feeState.feeIsIncluded
@@ -116,7 +118,7 @@ data class SendState(
}
}
-enum class SendButtonState {
+enum class ButtonState {
ENABLED, DISABLED, PROGRESS
}
@@ -131,7 +133,8 @@ data class AmountState(
val cursorAtTheSamePosition: Boolean = true,
val maxLengthOfAmount: Int = 2,
val decimalSeparator: String = ".",
- val error: TapError? = null
+ val error: TapError? = null,
+ val inputIsEnabled: Boolean = true,
) : SendScreenState {
override val stateId: StateId = StateId.AMOUNT
@@ -166,4 +169,12 @@ data class MainCurrency(
val type: MainCurrencyType,
val currencySymbol: String,
val isEnabled: Boolean = true
+)
+
+data class ExternalTransactionData(
+ val amount: String,
+ val destinationAddress: String,
+ val transactionId: String,
+ val canAmountBeModified: Boolean = false,
+ val canDestinationAddressBeModified: Boolean = false
)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt
index 2b2b682294..bb675cf3fe 100644
--- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt
@@ -7,6 +7,7 @@ import android.text.method.DigitsKeyListener
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
+import androidx.activity.OnBackPressedCallback
import androidx.core.view.postDelayed
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.LinearLayoutManager
@@ -20,6 +21,7 @@ import com.tangem.tap.common.entities.TapCurrency
import com.tangem.tap.common.extensions.getFromClipboard
import com.tangem.tap.common.extensions.setOnImeActionListener
import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity
+import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.common.snackBar.MaxAmountSnackbar
import com.tangem.tap.common.text.truncateMiddleWith
import com.tangem.tap.common.toggleWidget.*
@@ -31,6 +33,7 @@ import com.tangem.tap.features.send.redux.FeeActionUi.*
import com.tangem.tap.features.send.redux.states.FeeType
import com.tangem.tap.features.send.redux.states.MainCurrencyType
import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber
+import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.ui.adapters.SpacesItemDecoration
import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
import com.tangem.tap.mainScope
@@ -60,6 +63,23 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
private val sendSubscriber = SendStateSubscriber(this)
private lateinit var keyboardObserver: KeyboardObserver
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ val externalTransactionData = store.state.sendState.externalTransactionData
+ if (externalTransactionData == null) {
+ store.dispatch(NavigationAction.PopBackTo())
+ } else {
+ store.dispatch(
+ WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)
+ )
+ }
+ }
+ })
+ }
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -71,6 +91,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
setupAmountLayout()
setupFeeLayout()
setupWarningMessages()
+ store.dispatch(SendActionUi.CheckIfTransactionDataWasProvided)
}
private fun initSendButtonStates() {
@@ -250,7 +271,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
val sp = requireContext().getSharedPreferences("SendScreen", Context.MODE_PRIVATE)
val mainCurrency = sp.getString("mainCurrency", TapCurrency.DEFAULT_FIAT_CURRENCY)
val foundType = MainCurrencyType.values()
- .firstOrNull { it.name.toLowerCase() == mainCurrency!!.toLowerCase() }
+ .firstOrNull { it.name.equals(mainCurrency!!, ignoreCase = true) }
?: MainCurrencyType.CRYPTO
return foundType
}
diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt
index 933b1580d7..2471b257de 100644
--- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt
@@ -10,7 +10,6 @@ import androidx.core.text.bold
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.getMessageString
import com.tangem.tap.common.text.DecimalDigitsInputFilter
-import com.tangem.tap.common.toggleWidget.ProgressState
import com.tangem.tap.domain.MultiMessageError
import com.tangem.tap.domain.assembleErrors
import com.tangem.tap.features.send.BaseStoreFragment
@@ -116,20 +115,8 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
}
}
- when (state.sendButtonState) {
- SendButtonState.ENABLED -> {
- fg.btnSend.isEnabled = true
- sendFragment.sendBtn.changeState(ProgressState.None)
- }
- SendButtonState.DISABLED -> {
- fg.btnSend.isEnabled = false
- sendFragment.sendBtn.changeState(ProgressState.None)
- }
- SendButtonState.PROGRESS -> {
- fg.btnSend.isEnabled = true
- sendFragment.sendBtn.changeState(ProgressState.Progress())
- }
- }
+ sendFragment.sendBtn.changeState(state.sendButtonState.progressState)
+ sendFragment.sendBtn.mainView.isEnabled = state.sendButtonState.enabled
val rv = fg.rv_warning_messages
val adapter = rv.adapter as? WarningMessagesAdapter ?: return
@@ -156,6 +143,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
val til = fg.tilAddressOrPayId
val parsedError = parseError(til.context, state.error)
+ til.isEnabled = state.inputIsEnabled
+ fg.imvPaste.show(state.inputIsEnabled)
+ fg.imvQrCode.show(state.inputIsEnabled)
+ fg.flPaste.show(state.inputIsEnabled)
+ fg.flQrCode.show(state.inputIsEnabled)
+
val hintResId = if (state.sendingToPayIdEnabled) {
R.string.send_destination_hint_address_payid
} else {
@@ -179,7 +172,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
val messageList = multiError.assembleErrors().map { getMessageString(context, it.first, it.second) }
multiError.builder(messageList)
}
- else -> context.getString(state.error.localizedMessage)
+ else -> context.getString(state.error.messageResource)
}
fg.tilAmountToSend.enableError(true, message)
} else {
@@ -198,6 +191,14 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
state.mainCurrency.currencySymbol,
state.viewBalanceValue)
fg.tvBalance.update(balanceText)
+
+ fg.tilAmountToSend.isEnabled = state.inputIsEnabled
+
+ val imageRes = if (state.inputIsEnabled) R.drawable.ic_arrows_up_down else 0
+ fg.tvAmountCurrency.setCompoundDrawablesWithIntrinsicBounds(0, 0, imageRes, 0)
+ val textColor = if (state.inputIsEnabled) R.color.blue else R.color.textGray
+ fg.tvAmountCurrency.setTextColor(fg.getColor(textColor))
+
}
private fun handleFeeState(fg: BaseStoreFragment, state: FeeState) {
diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt
index cb13d64dd0..e8afbe2d62 100644
--- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt
@@ -2,6 +2,7 @@ package com.tangem.tap.features.tokens.redux
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.currenciesRepository
+import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.features.tokens.ui.adapters.CurrencyListItem
import com.tangem.tap.store
import org.rekotlin.Middleware
@@ -13,9 +14,13 @@ class TokensMiddleware {
{ action ->
when (action) {
is TokensAction.LoadCurrencies -> {
- val cardFirmware = state()?.globalState?.scanNoteResponse?.card?.firmwareVersion
- val tokens = currenciesRepository.getPopularTokens()
- val blockchains = currenciesRepository.getBlockchains(cardFirmware)
+ val card = state()?.globalState?.scanResponse?.card
+ val isTestcard = card?.isTestCard ?: false
+ val tokens = currenciesRepository.getPopularTokens(isTestcard)
+ val blockchains = currenciesRepository.getBlockchains(
+ cardFirmware = card?.firmwareVersion,
+ isTestNet = isTestcard
+ )
val currencies = CurrencyListItem.createListOfCurrencies(
blockchains, tokens
)
diff --git a/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt b/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt
index 7767d74b32..918dd2916a 100644
--- a/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt
+++ b/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt
@@ -10,8 +10,10 @@ import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
-import com.tangem.tap.common.extensions.*
-import com.tangem.tap.common.redux.global.CryptoCurrencyName
+import com.tangem.tap.common.extensions.getString
+import com.tangem.tap.common.extensions.hide
+import com.tangem.tap.common.extensions.loadCurrenciesIcon
+import com.tangem.tap.common.extensions.show
import com.tangem.tap.domain.tokens.CardCurrencies
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.store
@@ -84,17 +86,18 @@ class CurrenciesAdapter : ListAdapter
when (element) {
is CurrencyListItem.BlockchainListItem -> {
element.blockchain.currency.toLowerCase(Locale.US)
- .contains(queryNormalized)
- || element.blockchain.fullName.toLowerCase(Locale.US)
- .contains(queryNormalized)
+ .contains(queryNormalized) ||
+ element.blockchain.fullName.toLowerCase(Locale.US)
+ .contains(queryNormalized)
}
is CurrencyListItem.TokenListItem -> {
- element.token.name.toLowerCase(Locale.US).contains(queryNormalized)
- || element.token.symbol.toLowerCase(Locale.US)
- .contains(queryNormalized)
+ element.token.name.toLowerCase(Locale.US)
+ .contains(queryNormalized) ||
+ element.token.symbol.toLowerCase(Locale.US)
+ .contains(queryNormalized)
}
- else -> false
+ is CurrencyListItem.TitleListItem -> true
}
})
} else {
@@ -143,7 +146,7 @@ class CurrenciesAdapter : ListAdapter
Picasso.get().loadCurrenciesIcon(
imageView = view.iv_currency,
textView = view.tv_token_letter,
- token = token, blockchain = Blockchain.Ethereum
+ token = token, blockchain = token.blockchain
)
view.btn_add_token.setOnClickListener {
store.dispatch(WalletAction.MultiWallet.AddToken(token))
@@ -175,11 +178,21 @@ sealed class CurrencyListItem {
tokens: List
): List {
val blockchainsTitle = R.string.add_tokens_subtitle_blockchains
- val tokensTitle = R.string.add_tokens_subtitle_tokens
+ val ethereumTokensTitle = R.string.add_tokens_subtitle_ethereum_tokens
+ val bscTokensTitle = R.string.add_tokens_subtitle_bsc_tokens
+ val binanceTokensTitle = R.string.add_tokens_subtitle_binance_tokens
+
+ val ethereumTokens = tokens.filter { it.blockchain == Blockchain.Ethereum }
+ val bscTokens = tokens.filter { it.blockchain == Blockchain.BSC }
+ val binanceTokens = tokens.filter { it.blockchain == Blockchain.Binance }
return listOf(TitleListItem(blockchainsTitle)) +
blockchains.map { BlockchainListItem(it) } +
- listOf(TitleListItem(tokensTitle)) +
- tokens.map { TokenListItem(it) }
+ listOf(TitleListItem(ethereumTokensTitle)) +
+ ethereumTokens.map { TokenListItem(it) } +
+ listOf(TitleListItem(bscTokensTitle)) +
+ bscTokens.map { TokenListItem(it) } +
+ listOf(TitleListItem(binanceTokensTitle)) +
+ binanceTokens.map { TokenListItem(it) }
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt
index 0ade915979..492513e02c 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt
@@ -3,6 +3,7 @@ package com.tangem.tap.features.wallet.models
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.TransactionStatus
+import com.tangem.blockchain.common.Wallet
import com.tangem.tap.common.extensions.toFormattedString
data class PendingTransaction(
@@ -57,4 +58,12 @@ fun TransactionData.toPendingTransactionForToken(token: Token, walletAddress: St
fun List.toPendingTransactionsForToken(token: Token, walletAddress: String): List {
return this.mapNotNull { it.toPendingTransactionForToken(token, walletAddress) }
+}
+
+fun Wallet.getPendingTransactions(): List {
+ return recentTransactions.toPendingTransactions(address)
+}
+
+fun Wallet.hasPendingTransactions(): Boolean {
+ return recentTransactions.toPendingTransactions(address).isNotEmpty()
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt
index 4bd89c024b..6fd84ec7cd 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt
@@ -3,7 +3,7 @@ package com.tangem.tap.features.wallet.redux
import android.content.Context
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.address.AddressType
-import com.tangem.commands.common.card.Card
+import com.tangem.common.card.Card
import com.tangem.tap.common.redux.ErrorAction
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.domain.TapError
@@ -18,12 +18,18 @@ sealed class WalletAction : Action {
object ResetState : WalletAction()
+ data class SetIfTestnetCard(val isTestnet: Boolean) : WalletAction()
+
object LoadData : WalletAction() {
data class Failure(val error: TapError) : WalletAction()
}
- data class LoadWallet(val allowTopUp: Boolean? = null, val blockchain: Blockchain? = null) : WalletAction() {
+ data class LoadWallet(
+ val allowToSell: Boolean? = null, val allowToBuy: Boolean? = null,
+ val blockchain: Blockchain? = null,
+ ) :
+ WalletAction() {
data class Success(val wallet: Wallet) : WalletAction()
data class NoAccount(val wallet: Wallet, val amountToCreateAccount: String) : WalletAction()
data class Failure(val wallet: Wallet, val errorMessage: String? = null) : WalletAction()
@@ -37,13 +43,16 @@ sealed class WalletAction : Action {
data class AddWalletManagers(val walletManagers: List) : MultiWallet() {
constructor(walletManager: WalletManager) : this(listOf(walletManager))
}
+
data class AddBlockchain(val blockchain: Blockchain) : MultiWallet()
data class AddBlockchains(val blockchains: List) : MultiWallet()
data class AddTokens(val tokens: List) : MultiWallet()
data class AddToken(val token: Token) : MultiWallet()
data class SaveCurrencies(val cardCurrencies: CardCurrencies) : MultiWallet()
object FindTokensInUse : MultiWallet()
- data class FindBlockchainsInUse(val card: Card, val factory: WalletManagerFactory) : MultiWallet()
+ data class FindBlockchainsInUse(val card: Card, val factory: WalletManagerFactory) :
+ MultiWallet()
+
data class TokenLoaded(val amount: Amount, val token: Token) : MultiWallet()
data class SelectWallet(val walletData: WalletData?) : MultiWallet()
data class RemoveWallet(val walletData: WalletData) : MultiWallet()
@@ -67,6 +76,7 @@ sealed class WalletAction : Action {
object SetNeverToShow : Warnings()
object RemindLater : Warnings()
}
+
class CheckRemainingSignatures(val remainingSignatures: Int?) : Warnings()
}
@@ -77,12 +87,14 @@ sealed class WalletAction : Action {
}
data class LoadFiatRate(
- val wallet: Wallet? = null, val currency: Currency? = null
+ val wallet: Wallet? = null, val currency: Currency? = null,
) : WalletAction() {
data class Success(val fiatRate: Pair) : WalletAction()
object Failure : WalletAction()
}
+ class LoadCardInfo(val card: Card) : WalletAction()
+
data class LoadArtwork(val card: Card, val artworkId: String?) : WalletAction() {
data class Success(val artwork: Artwork) : WalletAction()
object Failure : WalletAction()
@@ -110,8 +122,8 @@ sealed class WalletAction : Action {
object ShowDialog : WalletAction() {
object QrCode : WalletAction()
- object ScanFails : WalletAction()
object SignedHashesMultiWalletDialog : WalletAction()
+ object ChooseTradeActionDialog : WalletAction()
}
object HideDialog : WalletAction()
@@ -121,8 +133,16 @@ sealed class WalletAction : Action {
object CreateWallet : WalletAction()
object EmptyWallet : WalletAction()
- sealed class TopUpAction : WalletAction() {
- data class TopUp(val context: Context, val toolbarColor: Int) : TopUpAction()
+ sealed class TradeCryptoAction : WalletAction() {
+ object Buy : TradeCryptoAction()
+ object Sell : TradeCryptoAction()
+ data class FinishSelling(val transactionId: String) : TradeCryptoAction()
+ data class SendCrypto(
+ val currencyId: String,
+ val amount: String,
+ val destinationAddress: String,
+ val transactionId: String
+ ) : TradeCryptoAction()
}
data class ChangeSelectedAddress(val type: AddressType) : WalletAction()
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt
index 5cfe80591f..d078cd0526 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt
@@ -5,10 +5,13 @@ import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.address.AddressType
import com.tangem.blockchain.extensions.isAboveZero
import com.tangem.tap.common.entities.Button
+import com.tangem.tap.common.extensions.toQrCode
+import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.global.CryptoCurrencyName
-import com.tangem.tap.common.redux.global.StateDialog
+import com.tangem.tap.common.toggleWidget.WidgetState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.extensions.toSendableAmounts
+import com.tangem.tap.domain.topup.TradeCryptoHelper
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.toPendingTransactions
@@ -32,7 +35,9 @@ data class WalletState(
val cardCurrency: CryptoCurrencyName? = null,
val selectedWallet: Currency? = null,
val primaryBlockchain: Blockchain? = null,
- val primaryToken: Token? = null
+ val primaryToken: Token? = null,
+ val isTestnet: Boolean = false,
+ val tradeCryptoAllowed: TradeCryptoAvailability = TradeCryptoAvailability()
) : StateType {
val primaryWallet = if (wallets.isNotEmpty()) wallets[0] else null
@@ -49,17 +54,7 @@ data class WalletState(
fun getWalletManager(token: Token?): WalletManager? {
if (token == null) return null
- val ethereumWalletManager = walletManagers.find { it.wallet.blockchain == Blockchain.Ethereum }
- return if (ethereumWalletManager?.presetTokens?.contains(token) == true) {
- ethereumWalletManager
- } else {
- val primaryWalletManager = walletManagers.find { it.wallet.blockchain == primaryBlockchain }
- if (primaryWalletManager?.presetTokens?.contains(token) == true) {
- primaryWalletManager
- } else {
- ethereumWalletManager
- }
- }
+ return walletManagers.find { it.wallet.blockchain == token.blockchain }
}
fun getWalletManager(currency: Currency?) : WalletManager? {
@@ -98,7 +93,7 @@ data class WalletState(
?: return true
if (walletData.currency is Currency.Blockchain &&
- walletManager.presetTokens.isNotEmpty()
+ walletManager.cardTokens.isNotEmpty()
) {
return false
}
@@ -148,11 +143,11 @@ sealed class WalletDialog: StateDialog {
) : WalletDialog()
data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog()
- object ScanFailsDialog : WalletDialog()
object SignedHashesMultiWalletDialog : WalletDialog()
+ object ChooseTradeActionDialog : WalletDialog()
}
-enum class ProgressState { Loading, Done, Error }
+enum class ProgressState: WidgetState { Loading, Done, Error }
enum class ErrorType { NoInternetConnection }
@@ -171,8 +166,9 @@ data class AddressData(
val type: AddressType,
val shareUrl: String,
val exploreUrl: String,
- val qrCode: Bitmap? = null
-)
+) {
+ val qrCode: Bitmap by lazy { shareUrl.toQrCode() }
+}
data class Artwork(
val artworkId: String,
@@ -186,13 +182,20 @@ data class Artwork(
const val MARTA_CARD_ID = "BC02"
const val TWIN_CARD_1 = "https://app.tangem.com/cards/card_tg085.png"
const val TWIN_CARD_2 = "https://app.tangem.com/cards/card_tg086.png"
+
+ const val TEMP_CARDANO = "https://verify.tangem.com/card/artwork?artworkId=card_ru039&CID=CB19000000040976&publicKey=0416E29423A6CC77CD07CBA52873E8F6F894B1AFB18EB3688ACC2C8D8E5AC84B80B0BA1B17B85E578E47044CE96BCFF3FB4499FA4941CAD3C1EF300A492B5B9659"
}
}
-data class TopUpState(
- val allowed: Boolean = true,
- val url: String? = null,
- val redirectUrl: String? = null
+data class TradeCryptoState(
+ val sellingAllowed: Boolean = false,
+ val buyingAllowed: Boolean = false,
+)
+
+data class TradeCryptoAvailability(
+ val sellingAllowed: Boolean = false,
+ val buyingAllowed: Boolean = false,
+ val availableToSell: Set = TradeCryptoHelper.AVAILABLE_TO_SELL,
)
data class TwinCardsState(
@@ -202,24 +205,21 @@ data class TwinCardsState(
)
data class WalletData(
- val pendingTransactions: List = emptyList(),
- val hashesCountVerified: Boolean? = null,
- val walletAddresses: WalletAddresses? = null,
- val currencyData: BalanceWidgetData = BalanceWidgetData(),
- val updatingWallet: Boolean = false,
- val topUpState: TopUpState = TopUpState(),
- val allowToSend: Boolean = true,
- val fiatRateString: String? = null,
- val fiatRate: BigDecimal? = null,
- val mainButton: WalletMainButton = WalletMainButton.SendButton(false),
- val currency: Currency? = null
+ val pendingTransactions: List = emptyList(),
+ val hashesCountVerified: Boolean? = null,
+ val walletAddresses: WalletAddresses? = null,
+ val currencyData: BalanceWidgetData = BalanceWidgetData(),
+ val updatingWallet: Boolean = false,
+ val tradeCryptoState: TradeCryptoState = TradeCryptoState(),
+ val allowToSend: Boolean = true,
+ val fiatRateString: String? = null,
+ val fiatRate: BigDecimal? = null,
+ val mainButton: WalletMainButton = WalletMainButton.SendButton(false),
+ val currency: Currency? = null
) {
fun shouldShowMultipleAddress(): Boolean {
val listOfAddresses = walletAddresses?.list ?: return false
- return (currency?.blockchain == Blockchain.Bitcoin ||
- currency?.blockchain == Blockchain.BitcoinTestnet ||
- currency?.blockchain == Blockchain.CardanoShelley) &&
- listOfAddresses.size > 1
+ return listOfAddresses.size > 1
}
}
@@ -229,9 +229,9 @@ sealed interface Currency {
val currencySymbol: CryptoCurrencyName
data class Token(
- val token: com.tangem.blockchain.common.Token,
- override val blockchain: com.tangem.blockchain.common.Blockchain
+ val token: com.tangem.blockchain.common.Token
) : Currency {
+ override val blockchain = token.blockchain
override val currencySymbol: CryptoCurrencyName = token.symbol
}
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt
index 905ecce398..026cc26e4d 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt
@@ -22,7 +22,7 @@ import kotlinx.coroutines.withContext
class MultiWalletMiddleware {
fun handle(
- action: WalletAction.MultiWallet, walletState: WalletState?, globalState: GlobalState?,
+ action: WalletAction.MultiWallet, walletState: WalletState?, globalState: GlobalState?,
) {
when (action) {
is WalletAction.MultiWallet.AddWalletManagers -> {
@@ -35,7 +35,7 @@ class MultiWalletMiddleware {
}
}
is WalletAction.MultiWallet.AddToken -> {
- globalState?.scanNoteResponse?.card?.cardId?.let {
+ globalState?.scanResponse?.card?.cardId?.let {
currenciesRepository.saveAddedToken(it, action.token)
}
addToken(action.token, walletState, globalState)
@@ -44,7 +44,7 @@ class MultiWalletMiddleware {
addTokens(action.tokens, walletState, globalState)
}
is WalletAction.MultiWallet.AddBlockchain -> {
- globalState?.scanNoteResponse?.card?.let { card ->
+ globalState?.scanResponse?.card?.let { card ->
currenciesRepository.saveAddedBlockchain(card.cardId, action.blockchain)
if (walletState?.blockchains?.contains(action.blockchain) != true) {
globalState.tapWalletManager.walletManagerFactory
@@ -57,14 +57,19 @@ class MultiWalletMiddleware {
store.dispatch(WalletAction.LoadWallet(blockchain = action.blockchain))
}
is WalletAction.MultiWallet.SaveCurrencies -> {
- val cardId = globalState?.scanNoteResponse?.card?.cardId
+ val cardId = globalState?.scanResponse?.card?.cardId
cardId?.let { currenciesRepository.saveCardCurrencies(it, action.cardCurrencies) }
}
is WalletAction.MultiWallet.RemoveWallet -> {
- val cardId = globalState?.scanNoteResponse?.card?.cardId
+ val cardId = globalState?.scanResponse?.card?.cardId
when (val currency = action.walletData.currency) {
is Currency.Blockchain -> {
- cardId?.let { currenciesRepository.removeBlockchain(it, currency.blockchain) }
+ cardId?.let {
+ currenciesRepository.removeBlockchain(
+ it,
+ currency.blockchain
+ )
+ }
}
is Currency.Token -> {
walletState?.getWalletManager(currency.token)
@@ -74,10 +79,11 @@ class MultiWalletMiddleware {
}
}
is WalletAction.MultiWallet.FindBlockchainsInUse -> {
- val cardFirmware = globalState?.scanNoteResponse?.card?.firmwareVersion
+ val cardFirmware = globalState?.scanResponse?.card?.firmwareVersion
val blockchains = currenciesRepository.getBlockchains(cardFirmware)
- .filterNot { walletState?.blockchains?.contains(it) == true }
- val walletManagers = action.factory.makeWalletManagersForApp(action.card, blockchains)
+ .filterNot { walletState?.blockchains?.contains(it) == true }
+ val walletManagers =
+ action.factory.makeWalletManagersForApp(action.card, blockchains)
scope.launch {
walletManagers.map { walletManager ->
@@ -89,11 +95,16 @@ class MultiWalletMiddleware {
if (coinAmount != null && !coinAmount.isZero()) {
scope.launch(Dispatchers.Main) {
if (walletState?.getWalletData(wallet.blockchain) == null) {
- store.dispatch(WalletAction.MultiWallet.AddWalletManagers(
- listOfNotNull(walletManager)))
- store.dispatch(WalletAction.MultiWallet.AddBlockchain(
- wallet.blockchain
- ))
+ store.dispatch(
+ WalletAction.MultiWallet.AddWalletManagers(
+ listOfNotNull(walletManager)
+ )
+ )
+ store.dispatch(
+ WalletAction.MultiWallet.AddBlockchain(
+ wallet.blockchain
+ )
+ )
store.dispatch(WalletAction.LoadWallet.Success(wallet))
}
}
@@ -105,12 +116,12 @@ class MultiWalletMiddleware {
}
}
is WalletAction.MultiWallet.FindTokensInUse -> {
- val card = globalState?.scanNoteResponse?.card ?: return
+ val card = globalState?.scanResponse?.card ?: return
val walletManager = walletState?.getWalletManager(Blockchain.Ethereum)
- ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp(
- card = card,
- blockchain = Blockchain.Ethereum
- )
+ ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp(
+ card = card,
+ blockchain = Blockchain.Ethereum
+ )
val tokenFinder = walletManager as TokenFinder
scope.launch {
val result = tokenFinder.findTokens()
@@ -131,7 +142,7 @@ class MultiWalletMiddleware {
)
store.dispatch(
WalletAction.MultiWallet.AddTokens(
- walletManager.presetTokens.toList()
+ walletManager.cardTokens.toList()
)
)
}
@@ -144,23 +155,18 @@ class MultiWalletMiddleware {
}
private fun addToken(token: Token, walletState: WalletState?, globalState: GlobalState?) {
- val card = globalState?.scanNoteResponse?.card ?: return
+ val card = globalState?.scanResponse?.card ?: return
val walletManager = walletState?.getWalletManager(token)
?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp(
card = card,
- blockchain = Blockchain.Ethereum
+ blockchain = token.blockchain
)?.also { walletManager ->
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain))
}
store.dispatch(
- WalletAction.LoadFiatRate(
- currency = Currency.Token(
- token = token,
- blockchain = walletManager?.wallet?.blockchain ?: Blockchain.Ethereum
- )
- )
+ WalletAction.LoadFiatRate(currency = Currency.Token(token))
)
scope.launch {
@@ -179,20 +185,19 @@ class MultiWalletMiddleware {
walletState: WalletState?,
globalState: GlobalState?,
) {
- val card = globalState?.scanNoteResponse?.card ?: return
+ val card = globalState?.scanResponse?.card ?: return
val tokensWithManagers = tokens.map { token ->
val walletManager = walletState?.getWalletManager(token)
?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp(
card = card,
- blockchain = Blockchain.Ethereum
+ blockchain = token.blockchain
)?.also { walletManager ->
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain))
}
- store.dispatch(WalletAction.LoadFiatRate(currency = Currency.Token(
- token = token,
- blockchain = walletManager?.wallet?.blockchain ?: Blockchain.Ethereum
- )))
+ store.dispatch(
+ WalletAction.LoadFiatRate(currency = Currency.Token(token))
+ )
TokenWithManager(token, walletManager)
}
scope.launch {
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt
deleted file mode 100644
index e0706570f7..0000000000
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.tangem.tap.features.wallet.redux.middlewares
-
-import android.net.Uri
-import androidx.browser.customtabs.CustomTabsIntent
-import com.tangem.tap.domain.TopUpHelper
-import com.tangem.tap.features.wallet.redux.WalletAction
-import com.tangem.tap.store
-
-class TopUpMiddleware {
- fun handle(action: WalletAction.TopUpAction) {
- when (action) {
- is WalletAction.TopUpAction.TopUp -> {
- val selectedWalletData = store.state.walletState.getSelectedWalletData()
- val config = store.state.globalState.configManager?.config ?: return
- val addresses = selectedWalletData?.walletAddresses ?: return
- if (addresses.list.isEmpty()) return
-
- val defaultAddress = addresses.list[0].address
- val url = TopUpHelper.getUrl(
- selectedWalletData.currencyData.currencySymbol!!,
- defaultAddress,
- config.moonPayApiKey,
- config.moonPayApiSecretKey
- )
- val customTabsIntent = CustomTabsIntent.Builder()
- .setToolbarColor(action.toolbarColor)
- .build()
- customTabsIntent.launchUrl(action.context, Uri.parse(url));
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt
new file mode 100644
index 0000000000..2b2087dd25
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt
@@ -0,0 +1,94 @@
+package com.tangem.tap.features.wallet.redux.middlewares
+
+import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
+import com.tangem.blockchain.common.AmountType
+import com.tangem.tap.common.extensions.dispatchOnMain
+import com.tangem.tap.common.redux.navigation.AppScreen
+import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.domain.topup.TopUpManager
+import com.tangem.tap.domain.topup.TradeCryptoHelper
+import com.tangem.tap.features.send.redux.PrepareSendScreen
+import com.tangem.tap.features.send.redux.SendAction
+import com.tangem.tap.features.wallet.redux.Currency
+import com.tangem.tap.features.wallet.redux.WalletAction
+import com.tangem.tap.scope
+import com.tangem.tap.store
+import kotlinx.coroutines.launch
+import timber.log.Timber
+
+
+class TradeCryptoMiddleware {
+ fun handle(action: WalletAction.TradeCryptoAction) {
+ when (action) {
+ is WalletAction.TradeCryptoAction.Buy -> startExchange(action)
+ is WalletAction.TradeCryptoAction.Sell -> startExchange(action)
+ is WalletAction.TradeCryptoAction.SendCrypto -> preconfigureAndOpenSendScreen(action)
+ is WalletAction.TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId)
+ }
+ }
+
+ private fun startExchange(action: WalletAction.TradeCryptoAction) {
+ val selectedWalletData = store.state.walletState.getSelectedWalletData()
+ val config = store.state.globalState.configManager?.config ?: return
+ val addresses = selectedWalletData?.walletAddresses ?: return
+ if (addresses.list.isEmpty()) return
+
+ val defaultAddress = addresses.list[0].address
+ val currency = selectedWalletData.currency
+ val currencySymbol = selectedWalletData.currency?.currencySymbol ?: return
+
+ val exchangeAction = if (action is WalletAction.TradeCryptoAction.Buy) {
+ TradeCryptoHelper.Action.Buy
+ } else {
+ TradeCryptoHelper.Action.Sell
+ }
+
+ if (exchangeAction == TradeCryptoHelper.Action.Buy &&
+ currency is Currency.Token && currency.blockchain.isTestnet()
+ ) {
+ val walletManager = store.state.walletState.getWalletManager(currency.token)
+ if (walletManager !is EthereumWalletManager) return
+
+ scope.launch {
+ TopUpManager().topUpTestErc20Tokens(
+ walletManager = walletManager, token = currency.token
+ )
+ }
+ return
+ }
+
+ val url = TradeCryptoHelper.getUrl(
+ exchangeAction,
+ currency?.blockchain,
+ currencySymbol,
+ defaultAddress,
+ config.moonPayApiKey,
+ config.moonPayApiSecretKey
+ )
+ Timber.d("Moonpay $exchangeAction URL: $url")
+ store.dispatchOnMain(NavigationAction.OpenUrl(url))
+ }
+
+ private fun preconfigureAndOpenSendScreen(action: WalletAction.TradeCryptoAction.SendCrypto) {
+ val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return
+ val walletManager =
+ store.state.walletState.getWalletManager(selectedWalletData.currency)
+ store.dispatchOnMain(PrepareSendScreen(
+ coinAmount = walletManager?.wallet?.amounts?.get(AmountType.Coin),
+ coinRate = selectedWalletData.fiatRate,
+ walletManager = walletManager
+ ))
+ store.dispatchOnMain(SendAction.SendSpecificTransaction(
+ sendAmount = action.amount,
+ destinationAddress = action.destinationAddress,
+ transactionId = action.transactionId
+ ))
+ store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Send))
+ }
+
+ private fun openReceiptUrl(transactionId: String) {
+ store.dispatchOnMain(NavigationAction.PopBackTo())
+ val url = TradeCryptoHelper.getSellCryptoReceiptUrl(transactionId)
+ store.dispatchOnMain(NavigationAction.OpenUrl(url))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt
index f1e3d5c0ae..3b3a4f31c1 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt
@@ -5,10 +5,14 @@ import android.net.Uri
import androidx.core.content.ContextCompat
import com.tangem.blockchain.common.*
import com.tangem.common.CompletionResult
+import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.isZero
+import com.tangem.common.services.Result
+import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.*
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.copyToClipboard
+import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.shareText
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
@@ -18,16 +22,16 @@ import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.domain.twins.TwinsHelper
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.details.redux.twins.CreateTwinWallet
+import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.network.NetworkStateChanged
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Middleware
class WalletMiddleware {
- private val topUpMiddleware = TopUpMiddleware()
+ private val tradeCryptoMiddleware = TradeCryptoMiddleware()
private val twinsMiddleware = TwinsMiddleware()
private val warningsMiddleware = WarningsMiddleware()
private val multiWalletMiddleware = MultiWalletMiddleware()
@@ -38,12 +42,11 @@ class WalletMiddleware {
val globalState = state()?.globalState
val walletState = state()?.walletState
when (action) {
- is WalletAction.TopUpAction -> topUpMiddleware.handle(action)
+ is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(action)
is WalletAction.TwinsAction -> twinsMiddleware.handle(action)
is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState)
is WalletAction.MultiWallet ->
multiWalletMiddleware.handle(action, walletState, globalState)
-
is WalletAction.LoadWallet -> {
scope.launch {
if (action.blockchain == null) {
@@ -93,7 +96,7 @@ class WalletMiddleware {
is WalletAction.CreateWallet -> {
if (walletState?.twinCardsState != null) {
store.dispatch(DetailsAction.CreateTwinWalletAction.ShowWarning(
- globalState?.scanNoteResponse?.card?.cardId?.let {
+ globalState?.scanResponse?.card?.cardId?.let {
TwinsHelper.getTwinCardNumber(it)
},
CreateTwinWallet.CreateWallet
@@ -101,22 +104,25 @@ class WalletMiddleware {
} else {
scope.launch {
val result = tangemSdkManager.createWallet(
- globalState?.scanNoteResponse?.card?.cardId
+ globalState?.scanResponse?.card?.cardId
)
when (result) {
is CompletionResult.Success -> {
- val card = globalState?.scanNoteResponse?.card?.let {
- result.data.copy(
- isPin1Default = it.isPin1Default,
- isPin2Default = it.isPin2Default,
- )
- } ?: result.data
val scanNoteResponse =
- globalState?.scanNoteResponse?.copy(card = card)
+ globalState?.scanResponse?.copy(card = result.data)
scanNoteResponse?.let {
globalState.tapWalletManager.onCardScanned(scanNoteResponse)
}
}
+ is CompletionResult.Failure -> {
+ (result.error as? TangemSdkError)?.let { error ->
+ FirebaseAnalyticsHandler.logCardSdkError(
+ error,
+ FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
+ card = store.state.detailsState.card
+ )
+ }
+ }
}
}
}
@@ -139,22 +145,38 @@ class WalletMiddleware {
}
is WalletAction.Scan -> {
+ store.dispatch(HomeAction.ShouldScanCardOnResume(true))
+ store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
+ }
+ is WalletAction.LoadCardInfo -> {
scope.launch {
- val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler)
- scope.launch(Dispatchers.Main) {
- store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result))
- when (result) {
- is CompletionResult.Success -> {
- tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card)
- globalState?.tapWalletManager?.onCardScanned(result.data, true)
- }
+ val response = OnlineCardVerifier().getCardInfo(
+ action.card.cardId, action.card.cardPublicKey
+ )
+ when (response) {
+ is Result.Success -> {
+ store.dispatchOnMain(
+ WalletAction.SetArtworkId(response.data.artwork?.id)
+ )
+ store.dispatchOnMain(
+ WalletAction.LoadArtwork(action.card, response.data.artwork?.id)
+ )
+ store.dispatchOnMain(
+ GlobalAction.SetIfCardVerifiedOnline(response.data.passed)
+ )
+ }
+ is Result.Failure -> {
+ store.dispatchOnMain(
+ GlobalAction.SetIfCardVerifiedOnline(false)
+ )
}
}
+ store.dispatchOnMain(WalletAction.Warnings.CheckIfNeeded)
}
}
is WalletAction.LoadData -> {
scope.launch {
- val scanNoteResponse = globalState?.scanNoteResponse
+ val scanNoteResponse = globalState?.scanResponse
?: return@launch
if (!walletState?.wallets.isNullOrEmpty()) {
globalState.tapWalletManager.reloadData(scanNoteResponse)
@@ -164,7 +186,7 @@ class WalletMiddleware {
}
}
is NetworkStateChanged -> {
- globalState?.scanNoteResponse?.let { scanNoteResponse ->
+ globalState?.scanResponse?.let { scanNoteResponse ->
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
scope.launch {
globalState.tapWalletManager.loadData(scanNoteResponse)
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt
index 0ff512dc45..39f84c1b09 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt
@@ -4,21 +4,20 @@ import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.SignatureCountValidator
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.extensions.SimpleResult
-import com.tangem.commands.common.card.Card
-import com.tangem.commands.common.card.CardType
-import com.tangem.commands.verification.VerifyCardState
-import com.tangem.common.extensions.getType
+import com.tangem.common.card.Card
+import com.tangem.common.card.FirmwareVersion
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.isGreaterThan
import com.tangem.tap.common.redux.global.GlobalState
+import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
import com.tangem.tap.domain.extensions.getSingleWallet
import com.tangem.tap.domain.extensions.hasSignedHashes
import com.tangem.tap.domain.extensions.remainingSignatures
import com.tangem.tap.domain.isMultiwalletAllowed
-import com.tangem.tap.domain.twins.isTwinCard
+import com.tangem.tap.domain.twins.isTangemTwin
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.network.NetworkConnectivity
import com.tangem.tap.preferencesStorage
@@ -41,8 +40,8 @@ class WarningsMiddleware {
}
is WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline -> checkHashesCountOnline()
is WalletAction.Warnings.CheckHashesCount.SaveCardId -> {
- val cardId = globalState?.scanNoteResponse?.card?.cardId
- cardId?.let { preferencesStorage.saveScannedCardId(it) }
+ val cardId = globalState?.scanResponse?.card?.cardId
+ cardId?.let { preferencesStorage.usedCardsPrefStorage.scanned(it) }
}
is WalletAction.Warnings.AppRating.RemindLater -> {
preferencesStorage.appRatingLaunchObserver.applyDelayedShowing()
@@ -82,18 +81,21 @@ class WarningsMiddleware {
}
private fun showCardWarningsIfNeeded(globalState: GlobalState?) {
- globalState?.scanNoteResponse?.card?.let { card ->
+ globalState?.scanResponse?.card?.let { card ->
globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local)
+ if (card.isTestCard) {
+ addWarningMessage(WarningMessagesManager.testCardWarning(), autoUpdate = true)
+ return@let
+ }
+
showWarningLowRemainingSignaturesIfNeeded(card)
- if (card.getType() != CardType.Release) {
+ if (card.firmwareVersion.type != FirmwareVersion.FirmwareType.Release) {
addWarningMessage(WarningMessagesManager.devCardWarning())
- } else if (!preferencesStorage.wasCardScannedBefore(card.cardId)) {
+ } else if (!preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) {
checkIfWarningNeeded(card)?.let { warning -> addWarningMessage(warning) }
}
- if (card.getType() == CardType.Release) {
- if (globalState.scanNoteResponse.verifyResponse?.verificationState ==
- VerifyCardState.VerifiedOffline
- ) {
+ if (card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release) {
+ if (!globalState.cardVerifiedOnline) {
addWarningMessage(WarningMessagesManager.onlineVerificationFailed())
}
}
@@ -117,7 +119,7 @@ class WarningsMiddleware {
private fun checkIfWarningNeeded(
card: Card,
): WarningMessage? {
- if (card.isTwinCard()) return null
+ if (card.isTangemTwin()) return null
if (card.isMultiwalletAllowed) {
return if (card.hasSignedHashes()) {
@@ -147,15 +149,15 @@ class WarningsMiddleware {
if (store.state.walletState.hashesCountVerified != false) return
if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) return
- val card = store.state.globalState.scanNoteResponse?.card
- if (card == null || preferencesStorage.wasCardScannedBefore(card.cardId)) return
+ val card = store.state.globalState.scanResponse?.card
+ if (card == null || preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) return
- if (card.isTwinCard() || card.isMultiwalletAllowed) return
+ if (card.isTangemTwin() || card.isMultiwalletAllowed) return
val validator = store.state.walletState.walletManagers.firstOrNull()
as? SignatureCountValidator
scope.launch {
- val signedHashes = card.getSingleWallet()?.signedHashes ?: 0
+ val signedHashes = card.getSingleWallet()?.totalSignedHashes ?: 0
val result = validator?.validateSignatureCount(signedHashes)
withContext(Dispatchers.Main) {
when (result) {
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt
index 2f90f29e0f..030c2e2ab6 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt
@@ -1,7 +1,6 @@
package com.tangem.tap.features.wallet.redux.reducers
import com.tangem.blockchain.common.AmountType
-import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.common.extensions.isZero
import com.tangem.tap.common.extensions.toFiatString
@@ -38,8 +37,12 @@ class MultiWalletReducer {
),
walletAddresses = createAddressList(wallet),
mainButton = WalletMainButton.SendButton(false),
- topUpState = TopUpState(allowed = false),
- currency = Currency.Blockchain(blockchain)
+ currency = Currency.Blockchain(blockchain),
+ tradeCryptoState = TradeCryptoState(
+ sellingAllowed = state.tradeCryptoAllowed.sellingAllowed &&
+ state.tradeCryptoAllowed.availableToSell.contains(blockchain.currency),
+ buyingAllowed = state.tradeCryptoAllowed.buyingAllowed
+ )
)
}
@@ -63,8 +66,12 @@ class MultiWalletReducer {
),
walletAddresses = createAddressList(wallet),
mainButton = WalletMainButton.SendButton(false),
- topUpState = TopUpState(allowed = false),
- currency = Currency.Blockchain(action.blockchain)
+ currency = Currency.Blockchain(action.blockchain),
+ tradeCryptoState = TradeCryptoState(
+ sellingAllowed = state.tradeCryptoAllowed.sellingAllowed &&
+ state.tradeCryptoAllowed.availableToSell.contains(action.blockchain.currency),
+ buyingAllowed = state.tradeCryptoAllowed.buyingAllowed
+ )
)
val newState = state.copy(wallets = state.replaceWalletInWallets(walletData))
if (wallet != null && wallet.amounts[AmountType.Coin]?.value != null) {
@@ -165,10 +172,11 @@ fun Token.toWallet(state: WalletState): WalletData? {
),
walletAddresses = walletAddresses,
mainButton = WalletMainButton.SendButton(false),
- topUpState = TopUpState(allowed = false),
- currency = Currency.Token(
- token = this,
- blockchain = walletManager?.blockchain ?: Blockchain.Ethereum
+ currency = Currency.Token(this),
+ tradeCryptoState = TradeCryptoState(
+ sellingAllowed = state.tradeCryptoAllowed.sellingAllowed &&
+ state.tradeCryptoAllowed.availableToSell.contains(this.symbol),
+ buyingAllowed = state.tradeCryptoAllowed.buyingAllowed
)
)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt
index 5daef27db2..d999216bba 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt
@@ -2,12 +2,11 @@ package com.tangem.tap.features.wallet.redux.reducers
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Wallet
-import com.tangem.commands.common.network.TangemService
-import com.tangem.common.extensions.toHexString
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.domain.TapError
+import com.tangem.tap.domain.extensions.getArtworkUrl
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.features.wallet.redux.*
@@ -39,40 +38,46 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
is WalletAction.MultiWallet -> newState = multiWalletReducer.reduce(action, newState)
is WalletAction.ResetState -> newState = WalletState()
+ is WalletAction.SetIfTestnetCard -> newState = newState.copy(isTestnet = action.isTestnet)
is WalletAction.EmptyWallet -> {
val creatingWalletAllowed = !(newState.twinCardsState != null &&
newState.twinCardsState?.isCreatingTwinCardsAllowed != true)
newState = newState.copy(
- state = ProgressState.Done,
- wallets = listOf(WalletData(
- currencyData = BalanceWidgetData(BalanceStatus.EmptyCard),
- mainButton = WalletMainButton.CreateWalletButton(creatingWalletAllowed),
- topUpState = TopUpState(false)
- )))
+ state = ProgressState.Done,
+ wallets = listOf(
+ WalletData(
+ currencyData = BalanceWidgetData(BalanceStatus.EmptyCard),
+ mainButton = WalletMainButton.CreateWalletButton(creatingWalletAllowed),
+ )
+ )
+ )
}
is WalletAction.LoadData.Failure -> {
when (action.error) {
is TapError.NoInternetConnection -> {
val wallets = newState.wallets
- .map {
- it.copy(currencyData = it.currencyData.copy(
- status = BalanceStatus.Unreachable
- ))
- }
+ .map {
+ it.copy(
+ currencyData = it.currencyData.copy(
+ status = BalanceStatus.Unreachable
+ )
+ )
+ }
newState = newState.copy(
- state = ProgressState.Error,
- error = ErrorType.NoInternetConnection,
- wallets = wallets
+ state = ProgressState.Error,
+ error = ErrorType.NoInternetConnection,
+ wallets = wallets
)
}
is TapError.UnknownBlockchain -> {
newState = newState.copy(
- state = ProgressState.Done,
- wallets = listOf(WalletData(
- currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain),
- topUpState = TopUpState(false)
- ))
+ state = ProgressState.Done,
+ wallets = listOf(
+ WalletData(
+ currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain),
+ )
+ )
)
}
}
@@ -80,73 +85,87 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
is WalletAction.LoadData -> {
newState = newState.copy(
- state = ProgressState.Loading,
- error = null,
+ state = ProgressState.Loading,
+ error = null,
)
}
is WalletAction.LoadWallet -> {
if (action.blockchain == null) {
val wallets = newState.wallets.map { wallet ->
+
wallet.copy(
- currencyData = wallet.currencyData.copy(
- status = BalanceStatus.Loading,
- currency = wallet.currencyData.currency,
- currencySymbol = wallet.currencyData.currencySymbol,
- ),
- mainButton = WalletMainButton.SendButton(false),
- topUpState = TopUpState(allowed = action.allowTopUp
- ?: wallet.topUpState.allowed)
+ currencyData = wallet.currencyData.copy(
+ status = BalanceStatus.Loading,
+ currency = wallet.currencyData.currency,
+ currencySymbol = wallet.currencyData.currencySymbol,
+ ),
+ mainButton = WalletMainButton.SendButton(false),
+ tradeCryptoState = TradeCryptoState(
+ sellingAllowed = action.allowToSell ?: wallet.tradeCryptoState.sellingAllowed &&
+ state.walletState.tradeCryptoAllowed.availableToSell.contains(wallet.currencyData.currencySymbol),
+ buyingAllowed = action.allowToBuy
+ ?: wallet.tradeCryptoState.buyingAllowed
+ )
)
}
+ val tradeCryptoAllowed = TradeCryptoAvailability(
+ sellingAllowed = action.allowToSell ?: false,
+ buyingAllowed = action.allowToBuy ?: false
+ )
newState = newState.copy(
- state = ProgressState.Loading,
- wallets = wallets
+ state = ProgressState.Loading,
+ wallets = wallets,
+ tradeCryptoAllowed = tradeCryptoAllowed
)
} else {
val walletManager = newState.getWalletManager(action.blockchain) ?: return newState
val blockchain = walletManager.wallet.blockchain
- val currencies = listOf(Currency.Blockchain(blockchain)) + walletManager.presetTokens.map {
- Currency.Token(token = it, blockchain = blockchain)
- }
+ val currencies = listOf(Currency.Blockchain(blockchain)) +
+ walletManager.cardTokens.map { Currency.Token(it) }
val newWallets = newState.wallets.filter { currencies.contains(it.currency) }
- .map { wallet ->
- wallet.copy(
- currencyData = wallet.currencyData.copy(
- status = BalanceStatus.Loading,
- currency = wallet.currencyData.currency,
- currencySymbol = wallet.currencyData.currencySymbol,
- ),
- mainButton = WalletMainButton.SendButton(false),
- topUpState = TopUpState(
- allowed = action.allowTopUp ?: wallet.topUpState.allowed
- )
+ .map { wallet ->
+ wallet.copy(
+ currencyData = wallet.currencyData.copy(
+ status = BalanceStatus.Loading,
+ currency = wallet.currencyData.currency,
+ currencySymbol = wallet.currencyData.currencySymbol,
+ ),
+ mainButton = WalletMainButton.SendButton(false),
+ tradeCryptoState = TradeCryptoState(
+ sellingAllowed = action.allowToSell ?: wallet.tradeCryptoState.sellingAllowed &&
+ state.walletState.tradeCryptoAllowed.availableToSell.contains(wallet.currencyData.currencySymbol),
+ buyingAllowed = action.allowToBuy
+ ?: wallet.tradeCryptoState.buyingAllowed
)
- }
+ )
+ }
val wallets = newState.replaceSomeWallets(newWallets)
newState = newState.copy(wallets = wallets)
}
}
- is WalletAction.LoadWallet.Success -> newState = onWalletLoadedReducer.reduce(action.wallet, newState)
+ is WalletAction.LoadWallet.Success -> newState =
+ onWalletLoadedReducer.reduce(action.wallet, newState)
is WalletAction.UpdateWallet.Success -> {
newState = onWalletLoadedReducer.reduce(action.wallet, newState)
}
is WalletAction.LoadWallet.NoAccount -> {
val walletData = newState.getWalletData(action.wallet.blockchain)?.copy(
- currencyData = BalanceWidgetData(
- BalanceStatus.NoAccount, action.wallet.blockchain.fullName,
- currencySymbol = action.wallet.blockchain.currency,
- amountToCreateAccount = action.amountToCreateAccount
- )
+ currencyData = BalanceWidgetData(
+ BalanceStatus.NoAccount, action.wallet.blockchain.fullName,
+ currencySymbol = action.wallet.blockchain.currency,
+ amountToCreateAccount = action.amountToCreateAccount
+ )
)
val wallets = newState.replaceWalletInWallets(walletData)
- val progressState = if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
- ProgressState.Loading
- } else {
- ProgressState.Done
- }
+ val progressState =
+ if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
+ ProgressState.Loading
+ } else {
+ ProgressState.Done
+ }
newState = newState.copy(
- state = progressState,
- wallets = wallets
+ state = progressState,
+ wallets = wallets
)
}
is WalletAction.LoadWallet.Failure -> {
@@ -157,28 +176,30 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
}
val walletData = newState.getWalletData(action.wallet.blockchain)
val newWalletData = walletData?.copy(
- currencyData = walletData.currencyData.copy(
- status = BalanceStatus.Unreachable,
- errorMessage = message
- ),
- topUpState = TopUpState(false)
+ currencyData = walletData.currencyData.copy(
+ status = BalanceStatus.Unreachable,
+ errorMessage = message
+ ),
)
val tokenWallets = action.wallet.getTokens()
- .mapNotNull { newState.getWalletData(it) }
- .map {
- it.copy(currencyData = it.currencyData.copy(
- status = BalanceStatus.Unreachable, errorMessage = message
- ))
- }
+ .mapNotNull { newState.getWalletData(it) }
+ .map {
+ it.copy(
+ currencyData = it.currencyData.copy(
+ status = BalanceStatus.Unreachable, errorMessage = message
+ )
+ )
+ }
val wallets = newState.replaceSomeWallets(listOfNotNull(newWalletData) + tokenWallets)
- val progressState = if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
- ProgressState.Loading
- } else {
- ProgressState.Done
- }
+ val progressState =
+ if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
+ ProgressState.Loading
+ } else {
+ ProgressState.Done
+ }
newState = newState.copy(
- state = progressState, wallets = wallets
+ state = progressState, wallets = wallets
)
}
is WalletAction.SetArtworkId -> {
@@ -193,60 +214,55 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
is WalletAction.LoadFiatRate.Success ->
newState = setNewFiatRate(action.fiatRate, state.globalState.appCurrency, newState)
is WalletAction.LoadArtwork -> {
- val cardId = action.card.cardId
- val cardPublicKey = action.card.cardPublicKey?.toHexString()
- val artworkUrl = if (cardPublicKey != null && action.artworkId != null) {
- TangemService.getUrlForArtwork(cardId, cardPublicKey, action.artworkId)
- } else if (action.card.cardId.startsWith(Artwork.SERGIO_CARD_ID)) {
- Artwork.SERGIO_CARD_URL
- } else if (action.card.cardId.startsWith(Artwork.MARTA_CARD_ID)) {
- Artwork.MARTA_CARD_URL
- } else if (newState.twinCardsState?.cardNumber != null) {
- when (newState.twinCardsState?.cardNumber) {
- TwinCardNumber.First -> Artwork.TWIN_CARD_1
- TwinCardNumber.Second -> Artwork.TWIN_CARD_2
- null -> Artwork.DEFAULT_IMG_URL
- }
- } else {
- Artwork.DEFAULT_IMG_URL
- }
+ val artworkUrl = action.card.getArtworkUrl(action.artworkId)
+ ?: when (newState.twinCardsState?.cardNumber) {
+ TwinCardNumber.First -> Artwork.TWIN_CARD_1
+ TwinCardNumber.Second -> Artwork.TWIN_CARD_2
+ else -> Artwork.DEFAULT_IMG_URL
+ }
newState = newState.copy(cardImage = Artwork(artworkId = artworkUrl))
}
is WalletAction.ShowDialog.QrCode -> {
val selectedWalletData = newState.getWalletData(newState.selectedWallet)
newState = newState.copy(
- walletDialog = WalletDialog.QrDialog(
- selectedWalletData?.walletAddresses?.selectedAddress?.shareUrl?.toQrCode(),
- selectedWalletData?.walletAddresses?.selectedAddress?.shareUrl,
- selectedWalletData?.currencyData?.currency
- )
+ walletDialog = WalletDialog.QrDialog(
+ selectedWalletData?.walletAddresses?.selectedAddress?.shareUrl?.toQrCode(),
+ selectedWalletData?.walletAddresses?.selectedAddress?.shareUrl,
+ selectedWalletData?.currencyData?.currency
+ )
)
}
- is WalletAction.ShowDialog.ScanFails -> {
- newState = newState.copy(walletDialog = WalletDialog.ScanFailsDialog)
- }
is WalletAction.ShowDialog.SignedHashesMultiWalletDialog -> {
newState = newState.copy(walletDialog = WalletDialog.SignedHashesMultiWalletDialog)
}
+ is WalletAction.ShowDialog.ChooseTradeActionDialog -> {
+ newState = newState.copy(walletDialog = WalletDialog.ChooseTradeActionDialog)
+ }
is WalletAction.HideDialog -> {
newState = newState.copy(walletDialog = null)
}
is WalletAction.Send.ChooseCurrency -> {
newState = newState.copy(
- walletDialog = WalletDialog.SelectAmountToSendDialog(action.amounts)
+ walletDialog = WalletDialog.SelectAmountToSendDialog(action.amounts)
)
}
is WalletAction.Send.Cancel -> newState = newState.copy(walletDialog = null)
- is WalletAction.TopUpAction -> return newState
+ is WalletAction.TradeCryptoAction -> return newState
is WalletAction.ChangeSelectedAddress -> {
val selectedWalletData = newState.getWalletData(newState.selectedWallet)
- val walletAddresses = newState.getWalletData(selectedWalletData?.currency)?.walletAddresses
+ val walletAddresses =
+ newState.getWalletData(selectedWalletData?.currency)?.walletAddresses
?: return newState
val address = walletAddresses.list.firstOrNull { it.type == action.type }
- ?: return newState
+ ?: return newState
val wallets = newState.replaceWalletInWallets(
- selectedWalletData?.copy(walletAddresses = WalletAddresses(address, walletAddresses.list))
+ selectedWalletData?.copy(
+ walletAddresses = WalletAddresses(
+ address,
+ walletAddresses.list
+ )
+ )
)
newState = newState.copy(wallets = wallets)
}
@@ -257,30 +273,45 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
fun createAddressList(wallet: Wallet?, walletAddresses: WalletAddresses? = null): WalletAddresses? {
if (wallet == null) return null
- val listOfAddressData = mutableListOf()
- // put a defaultAddress at the first place
- wallet.addresses.forEach {
- val addressData = AddressData(it.value, it.type, wallet.getShareUri(it.value), wallet.getExploreUrl(it.value))
- if (it.type == wallet.blockchain.defaultAddressType()) {
- listOfAddressData.add(0, addressData)
- } else {
- listOfAddressData.add(addressData)
- }
- }
-
+ val listOfAddressData = wallet.createAddressesData()
// restore a selected wallet address
var indexOfSelectedWallet = 0
walletAddresses?.let {
- val index = listOfAddressData.indexOfFirst { it.address == walletAddresses.selectedAddress.address }
+ val index =
+ listOfAddressData.indexOfFirst { it.address == walletAddresses.selectedAddress.address }
if (index != -1) indexOfSelectedWallet = index
}
return WalletAddresses(listOfAddressData[indexOfSelectedWallet], listOfAddressData)
}
-private fun handleCheckSignedHashesActions(action: WalletAction.Warnings, state: WalletState): WalletState {
+fun Wallet.createAddressesData(): List {
+ val listOfAddressData = mutableListOf()
+ // put a defaultAddress at the first place
+ addresses.forEach {
+ val addressData = AddressData(
+ it.value,
+ it.type,
+ getShareUri(it.value),
+ getExploreUrl(it.value)
+ )
+ if (it.type == blockchain.defaultAddressType()) {
+ listOfAddressData.add(0, addressData)
+ } else {
+ listOfAddressData.add(addressData)
+ }
+ }
+ return listOfAddressData
+}
+
+private fun handleCheckSignedHashesActions(
+ action: WalletAction.Warnings,
+ state: WalletState
+): WalletState {
return when (action) {
WalletAction.Warnings.CheckHashesCount.ConfirmHashesCount -> state.copy(hashesCountVerified = true)
- WalletAction.Warnings.CheckHashesCount.NeedToCheckHashesCountOnline -> state.copy(hashesCountVerified = false)
+ WalletAction.Warnings.CheckHashesCount.NeedToCheckHashesCountOnline -> state.copy(
+ hashesCountVerified = false
+ )
is WalletAction.Warnings.Set -> state.copy(mainWarningsList = action.warningList)
else -> state
}
@@ -288,8 +319,8 @@ private fun handleCheckSignedHashesActions(action: WalletAction.Warnings, state:
private fun setNewFiatRate(
- fiatRate: Pair,
- appCurrency: FiatCurrencyName, state: WalletState
+ fiatRate: Pair,
+ appCurrency: FiatCurrencyName, state: WalletState
): WalletState {
val rate = fiatRate.second ?: return state
val rateFormatted = rate.toFormattedCurrencyString(2, appCurrency, RoundingMode.HALF_UP)
@@ -326,33 +357,34 @@ private fun setMultiWalletFiatRate(
}
private fun setSingeWalletFiatRate(
- rate: BigDecimal, rateFormatted: String, currency: Currency,
- appCurrency: FiatCurrencyName, state: WalletState
+ rate: BigDecimal, rateFormatted: String, currency: Currency,
+ appCurrency: FiatCurrencyName, state: WalletState
): WalletState {
val wallet = state.walletManagers[0].wallet
val token = wallet.getFirstToken()
if (currency == state.primaryWallet?.currency) {
val fiatAmount = wallet.amounts[AmountType.Coin]?.value
- ?.toFiatString(rate, appCurrency)
+ ?.toFiatString(rate, appCurrency)
val walletData = state.primaryWallet.copy(
- currencyData = state.primaryWallet.currencyData.copy(fiatAmountFormatted = fiatAmount),
- fiatRate = rate,
- fiatRateString = rateFormatted
+ currencyData = state.primaryWallet.currencyData.copy(fiatAmountFormatted = fiatAmount),
+ fiatRate = rate,
+ fiatRateString = rateFormatted
)
return state.copy(wallets = listOf(walletData))
} else if (currency is Currency.Token && currency.token == token) {
val tokenFiatAmount = wallet.getTokenAmount(token)?.value?.toFiatString(rate, appCurrency)
val tokenData = state.primaryWallet?.currencyData?.token?.copy(
- fiatAmount = tokenFiatAmount,
- fiatRate = rate,
- fiatRateString = rateFormatted
+ fiatAmount = tokenFiatAmount,
+ fiatRate = rate,
+ fiatRateString = rateFormatted
)
val walletData = state.primaryWallet?.copy(
- currencyData = state.primaryWallet.currencyData.copy(
- token = tokenData
- ))
+ currencyData = state.primaryWallet.currencyData.copy(
+ token = tokenData
+ )
+ )
val wallets = walletData?.let { listOf(walletData) } ?: emptyList()
return state.copy(wallets = wallets)
}
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/TwinsOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt
similarity index 95%
rename from app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/TwinsOnboardingFragment.kt
rename to app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt
index 1daec73f5b..7ecd724f9e 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/TwinsOnboardingFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt
@@ -1,4 +1,4 @@
-package com.tangem.tap.features.wallet.ui.dialogs
+package com.tangem.tap.features.wallet.ui
import android.os.Bundle
import android.view.View
@@ -15,7 +15,7 @@ import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_twin_cards.*
import kotlinx.android.synthetic.main.layout_twin_cards.*
-class TwinsOnboardingFragment : Fragment(R.layout.fragment_twin_cards) {
+class OnboardingTwinsFragment : Fragment(R.layout.fragment_twin_cards) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt
index 16f8f2b17b..1592ae4eb2 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt
@@ -12,7 +12,7 @@ import androidx.transition.TransitionInflater
import com.squareup.picasso.Picasso
import com.tangem.tap.common.SnackbarHandler
import com.tangem.tap.common.extensions.*
-import com.tangem.tap.common.redux.global.StateDialog
+import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.redux.*
@@ -86,11 +86,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
btn_share.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) }
- btn_top_up.setOnClickListener {
- store.dispatch(
- WalletAction.TopUpAction.TopUp(requireContext(), R.color.backgroundLightGray)
- )
- }
+ btn_top_up.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Buy) }
+
+ btn_sell.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Sell) }
}
override fun newState(state: WalletState) {
@@ -99,6 +97,14 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
val selectedWallet = state.getSelectedWalletData() ?: return
tv_currency_title.text = selectedWallet.currencyData.currency
+ val currency = selectedWallet.currency
+ if (currency is Currency.Token) {
+ tv_currency_subtitle.text = currency.blockchain.tokenDisplayName()
+ tv_currency_subtitle.show()
+ } else {
+ tv_currency_subtitle.hide()
+ }
+
showPendingTransactionsIfPresent(selectedWallet.pendingTransactions)
setupAddressCard(selectedWallet)
@@ -123,8 +129,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
srl_wallet_details.setOnRefreshListener {
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
store.dispatch(WalletAction.LoadWallet(
- selectedWallet.topUpState.allowed,
- selectedWallet.currency?.blockchain
+ allowToBuy = selectedWallet.tradeCryptoState.buyingAllowed,
+ allowToSell = selectedWallet.tradeCryptoState.sellingAllowed,
+ blockchain = selectedWallet.currency?.blockchain
))
}
}
@@ -132,6 +139,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
srl_wallet_details.isRefreshing = false
}
+
+ btn_top_up.isEnabled = selectedWallet.tradeCryptoState.buyingAllowed
+ btn_sell.show(selectedWallet.tradeCryptoState.sellingAllowed)
}
private fun handleCurrencyIcon(wallet: WalletData) {
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt
index 98c2b0d6fe..dbd285226f 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt
@@ -159,7 +159,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber {
store.dispatch(GlobalAction.UpdateFeedbackInfo(store.state.walletState.walletManagers))
- store.state.globalState.scanNoteResponse?.let { scanNoteResponse ->
+ store.state.globalState.scanResponse?.let { scanNoteResponse ->
store.dispatch(DetailsAction.PrepareScreen(
scanNoteResponse.card, scanNoteResponse,
store.state.walletState.walletManagers.map { it.wallet },
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt
index 62e9849d04..b90adc2cb6 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt
@@ -98,7 +98,7 @@ class WalletAdapter
Picasso.get().loadCurrenciesIcon(
imageView = view.iv_currency,
textView = view.tv_token_letter,
- token = token, blockchain = blockchain
+ token = token, blockchain = blockchain,
)
when (wallet.currencyData.status) {
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt
index c262bbb35c..2b793466c3 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt
@@ -73,7 +73,7 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) {
}
private fun setupControlButtons(warning: WarningMessage) = when (warning.type) {
- WarningMessage.Type.Permanent -> {
+ WarningMessage.Type.Permanent, WarningMessage.Type.TestCard -> {
view.group_controls_temporary.hide()
view.group_controls_rating.hide()
}
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt
new file mode 100644
index 0000000000..95ff1f65ea
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt
@@ -0,0 +1,32 @@
+package com.tangem.tap.features.wallet.ui.dialogs
+
+import android.content.Context
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.tangem.tap.features.wallet.redux.WalletAction
+import com.tangem.tap.store
+import com.tangem.wallet.R
+import kotlinx.android.synthetic.main.dialog_wallet_trade.*
+
+class ChooseTradeActionDialog(context: Context) : BottomSheetDialog(context) {
+
+ init {
+ this.setContentView(R.layout.dialog_wallet_trade)
+ }
+
+ override fun show() {
+ super.show()
+
+ this.setOnDismissListener {
+ store.dispatch(WalletAction.HideDialog)
+ }
+
+ dialog_btn_buy.setOnClickListener {
+ store.dispatch(WalletAction.TradeCryptoAction.Buy)
+ dismiss()
+ }
+ dialog_btn_sell.setOnClickListener {
+ store.dispatch(WalletAction.TradeCryptoAction.Sell)
+ dismiss()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt
index b3f63a0236..0f164965d4 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt
@@ -2,10 +2,9 @@ package com.tangem.tap.features.wallet.ui.dialogs
import android.content.Context
import androidx.appcompat.app.AlertDialog
+import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.feedback.ScanFailsEmail
-import com.tangem.tap.features.home.redux.HomeAction
-import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.store
import com.tangem.wallet.R
@@ -23,10 +22,7 @@ class ScanFailsDialog {
store.dispatch(GlobalAction.SendFeedback(ScanFailsEmail()))
}
setNegativeButton(R.string.common_cancel) { _, _ -> }
- setOnDismissListener {
- store.dispatch(HomeAction.HideDialog)
- store.dispatch(WalletAction.HideDialog)
- }
+ setOnDismissListener { store.dispatchDialogHide() }
}.create()
}
}
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt
index 5deff50687..aa0aaee36c 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt
@@ -4,7 +4,7 @@ import android.app.Dialog
import androidx.recyclerview.widget.LinearLayoutManager
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
-import com.tangem.tap.common.redux.global.StateDialog
+import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.tokens.redux.TokensAction
@@ -14,7 +14,6 @@ import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.tap.features.wallet.ui.adapters.WalletAdapter
-import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog
import com.tangem.tap.store
import com.tangem.wallet.R
@@ -42,7 +41,6 @@ class MultiWalletView : WalletView {
private fun showMultiWalletView(fragment: WalletFragment) = with(fragment) {
tv_twin_card_number.hide()
- iv_twin_card.hide()
rv_pending_transaction.hide()
l_card_balance.hide()
l_address.hide()
@@ -135,9 +133,6 @@ class MultiWalletView : WalletView {
val fragment = fragment ?: return
val context = fragment.context ?: return
when (walletDialog) {
- is WalletDialog.ScanFailsDialog -> {
- if (dialog == null) dialog = ScanFailsDialog.create(context).apply { show() }
- }
is WalletDialog.SignedHashesMultiWalletDialog -> {
if (dialog == null) {
dialog = SignedHashesWarningDialog.create(context).apply { show() }
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt
index ce0623ac86..d2e4e77cc5 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt
@@ -6,7 +6,7 @@ import android.view.ViewGroup
import android.widget.Button
import androidx.recyclerview.widget.LinearLayoutManager
import com.tangem.tap.common.extensions.*
-import com.tangem.tap.common.redux.global.StateDialog
+import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.twins.TwinCardNumber
@@ -16,10 +16,7 @@ import com.tangem.tap.features.wallet.ui.BalanceWidget
import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper
import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
-import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog
-import com.tangem.tap.features.wallet.ui.dialogs.QrDialog
-import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
-import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog
+import com.tangem.tap.features.wallet.ui.dialogs.*
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.card_balance.*
@@ -99,7 +96,6 @@ class SingleWalletView : WalletView {
) = with(fragment) {
twinCardsState?.cardNumber?.let { cardNumber ->
tv_twin_card_number.show()
- iv_twin_card.show()
val number = when (cardNumber) {
TwinCardNumber.First -> "1"
TwinCardNumber.Second -> "2"
@@ -109,10 +105,9 @@ class SingleWalletView : WalletView {
}
if (twinCardsState?.cardNumber == null) {
tv_twin_card_number.hide()
- iv_twin_card.hide()
}
if (twinCardsState?.showTwinOnboarding == true) {
- store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding))
+ store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))
}
@@ -124,12 +119,17 @@ class SingleWalletView : WalletView {
setupButtonsType(state, fragment)
- val btnConfirm = if (state.topUpState.allowed) {
+ val btnConfirm = if (state.tradeCryptoState.sellingAllowed ||
+ state.tradeCryptoState.buyingAllowed
+ ) {
btn_confirm_short
} else {
btn_confirm_long
}
- val btnScan = if (state.topUpState.allowed) {
+
+ val btnScan = if (state.tradeCryptoState.sellingAllowed ||
+ state.tradeCryptoState.buyingAllowed
+ ) {
btn_scan_short
} else {
btn_scan_long
@@ -149,14 +149,26 @@ class SingleWalletView : WalletView {
btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) }
btn_top_up.setOnClickListener {
- store.dispatch(
- WalletAction.TopUpAction.TopUp(fragment.requireContext(), R.color.backgroundLightGray)
- )
+ tradeCryptoAction(state.tradeCryptoState)
}
}
+ private fun tradeCryptoAction(tradeCryptoState: TradeCryptoState) {
+ val allowedToBuy = tradeCryptoState.buyingAllowed
+ val allowedToSell = tradeCryptoState.sellingAllowed
+ val action = when {
+ allowedToBuy && !allowedToSell -> WalletAction.TradeCryptoAction.Buy
+ !allowedToBuy && allowedToSell -> WalletAction.TradeCryptoAction.Sell
+ allowedToBuy && allowedToSell -> WalletAction.ShowDialog.ChooseTradeActionDialog
+ else -> null
+ }
+ if (action != null) store.dispatch(action)
+ }
+
private fun setupButtonsType(state: WalletData, fragment: WalletFragment) = with(fragment) {
- if (state.topUpState.allowed) {
+ if (state.tradeCryptoState.sellingAllowed ||
+ state.tradeCryptoState.buyingAllowed
+ ) {
l_buttons_long.hide()
l_buttons_short.show()
} else {
@@ -236,14 +248,14 @@ class SingleWalletView : WalletView {
this.show(walletDialog.amounts)
}
}
- is WalletDialog.ScanFailsDialog -> {
- if (dialog == null) dialog = ScanFailsDialog.create(context).apply { show() }
- }
is WalletDialog.SignedHashesMultiWalletDialog -> {
if (dialog == null) {
dialog = SignedHashesWarningDialog.create(context).apply { show() }
}
}
+ is WalletDialog.ChooseTradeActionDialog -> {
+ if (dialog == null) dialog = ChooseTradeActionDialog(context).apply { show() }
+ }
null -> {
dialog?.dismiss()
dialog = null
diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt
index fffc439820..cf497373cd 100644
--- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt
+++ b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt
@@ -1,7 +1,7 @@
package com.tangem.tap.network.coinmarketcap
-import com.tangem.commands.common.network.Result
-import com.tangem.commands.common.network.performRequest
+import com.tangem.common.services.Result
+import com.tangem.common.services.performRequest
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.store
import kotlinx.coroutines.Dispatchers
diff --git a/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt
new file mode 100644
index 0000000000..f3e062abf3
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt
@@ -0,0 +1,16 @@
+package com.tangem.tap.network.moonpay
+
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface MoonpayApi {
+ @GET( MOOONPAY_IP_ADDRESS_REQUEST_URL)
+ suspend fun getUserStatus(
+ @Query("apiKey") moonpayApiKey: String
+ ): MoonPayUserStatus
+
+ companion object {
+ const val MOOONPAY_BASE_URL = "https://api.moonpay.com/v4/"
+ const val MOOONPAY_IP_ADDRESS_REQUEST_URL = "ip_address/"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt
new file mode 100644
index 0000000000..9ef4b8b678
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt
@@ -0,0 +1,24 @@
+package com.tangem.tap.network.moonpay
+
+import com.squareup.moshi.JsonClass
+import com.tangem.common.services.Result
+import com.tangem.common.services.performRequest
+import com.tangem.tap.network.createRetrofitInstance
+
+class MoonpayService {
+
+ private val moonpayApi: MoonpayApi by lazy {
+ createRetrofitInstance(MoonpayApi.MOOONPAY_BASE_URL)
+ .create(MoonpayApi::class.java)
+ }
+
+ suspend fun getUserStatus(moonpayApiKey: String): Result {
+ return performRequest { moonpayApi.getUserStatus(moonpayApiKey) }
+ }
+}
+
+@JsonClass(generateAdapter = true)
+data class MoonPayUserStatus(
+ val isBuyAllowed: Boolean,
+ val isSellAllowed: Boolean
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/network/payid/PayIdService.kt b/app/src/main/java/com/tangem/tap/network/payid/PayIdService.kt
index 2e691228ea..6eab298c6f 100644
--- a/app/src/main/java/com/tangem/tap/network/payid/PayIdService.kt
+++ b/app/src/main/java/com/tangem/tap/network/payid/PayIdService.kt
@@ -1,8 +1,8 @@
package com.tangem.tap.network.payid
import com.squareup.moshi.JsonClass
-import com.tangem.commands.common.network.Result
-import com.tangem.commands.common.network.performRequest
+import com.tangem.common.services.Result
+import com.tangem.common.services.performRequest
import com.tangem.tap.network.createRetrofitInstance
import retrofit2.Retrofit
diff --git a/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt b/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt
index 109dc5e97f..13d79d4591 100644
--- a/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt
+++ b/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt
@@ -1,7 +1,7 @@
package com.tangem.tap.network.payid
-import com.tangem.commands.common.network.Result
-import com.tangem.commands.common.network.performRequest
+import com.tangem.common.services.Result
+import com.tangem.common.services.performRequest
import com.tangem.tap.network.createRetrofitInstance
/**
diff --git a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt
index 871e3eb3d5..12d5eaa695 100644
--- a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt
+++ b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt
@@ -19,10 +19,13 @@ class PreferencesStorage(applicationContext: Application) {
private val preferences: SharedPreferences = applicationContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
val appRatingLaunchObserver: AppRatingLaunchObserver
+ val usedCardsPrefStorage: UsedCardsPrefStorage
init {
incrementLaunchCounter()
appRatingLaunchObserver = AppRatingLaunchObserver(preferences, getCountOfLaunches())
+ usedCardsPrefStorage = UsedCardsPrefStorage(preferences)
+ usedCardsPrefStorage.migrate()
}
private val fiatCurrenciesAdapter: JsonAdapter> by lazy {
@@ -54,20 +57,11 @@ class PreferencesStorage(applicationContext: Application) {
fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1)
- fun saveScannedCardId(cardId: String) {
- val scannedCardsIds: String = restoreScannedCardIds()
- if (!scannedCardsIds.contains(cardId)) {
- preferences.edit().putString(SCANNED_CARDS_IDS_KEY, "$scannedCardsIds$cardId, ").apply()
- }
- }
-
+ @Deprecated("Use UsedCardsPrefStorage instead")
fun wasCardScannedBefore(cardId: String): Boolean {
- return restoreScannedCardIds().contains(cardId)
+ return usedCardsPrefStorage.wasScanned(cardId)
}
- private fun restoreScannedCardIds(): String =
- preferences.getString(SCANNED_CARDS_IDS_KEY, "") ?: ""
-
fun saveDisclaimerAccepted() {
preferences.edit().putBoolean(DISCLAIMER_ACCEPTED_KEY, true).apply()
}
@@ -93,7 +87,6 @@ class PreferencesStorage(applicationContext: Application) {
private const val PREFERENCES_NAME = "tapPrefs"
private const val APP_CURRENCY_KEY = "appCurrency"
private const val FIAT_CURRENCIES_KEY = "fiatCurrencies"
- private const val SCANNED_CARDS_IDS_KEY = "scannedCardIds"
private const val DISCLAIMER_ACCEPTED_KEY = "disclaimerAccepted"
private const val TWINS_ONBOARDING_SHOWN_KEY = "twinsOnboardingShown"
private const val APP_LAUNCH_COUNT_KEY = "launchCount"
@@ -102,20 +95,23 @@ class PreferencesStorage(applicationContext: Application) {
}
class AppRatingLaunchObserver(
- private val preferences: SharedPreferences,
- private val launchCounts: Int,
+ private val preferences: SharedPreferences,
+ private val launchCounts: Int,
) {
private val K_SHOW_RATING_AT_LAUNCH_COUNT = "showRatingDialogAtLaunchCount"
private val K_FUNDS_FOUND_DATE = "fundsFoundDate"
private val K_USER_WAS_INTERACT_WITH_RATING = "userWasInteractWithRating"
+ private val FUNDS_FOUND_DATE_UNDEFINED = -1L
private val deferShowing = 20
private val firstShowing = 3
private var fundsFoundDate: Calendar? = null
init {
- val dateTimeMs = preferences.getLong(K_FUNDS_FOUND_DATE, -1)
- if (dateTimeMs > 0) fundsFoundDate = Calendar.getInstance().apply { timeInMillis = dateTimeMs }
+ val msWhenFundsWasFound = preferences.getLong(K_FUNDS_FOUND_DATE, FUNDS_FOUND_DATE_UNDEFINED)
+ if (msWhenFundsWasFound != FUNDS_FOUND_DATE_UNDEFINED) {
+ fundsFoundDate = Calendar.getInstance().apply { timeInMillis = msWhenFundsWasFound }
+ }
}
fun foundWalletWithFunds() {
diff --git a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt
new file mode 100644
index 0000000000..17238689f5
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt
@@ -0,0 +1,102 @@
+package com.tangem.tap.persistence
+
+import android.content.SharedPreferences
+import androidx.core.content.edit
+import com.tangem.common.json.MoshiJsonConverter
+import com.tangem.tap.common.extensions.replaceByOrAdd
+import timber.log.Timber
+
+/**
+[REDACTED_AUTHOR]
+ */
+class UsedCardsPrefStorage(
+ private val preferences: SharedPreferences,
+) {
+
+ private val jsonConverter = MoshiJsonConverter.INSTANCE
+
+ internal fun migrate() {
+ val scannedIds = preferences.getString(SCANNED_CARDS_IDS_KEY, null) ?: return
+
+ val usedCardsInfo = scannedIds.split(",").map { UsedCardInfo(it.trim(), true) }
+ if (save(usedCardsInfo.toMutableList())) {
+ preferences.edit { this.remove(SCANNED_CARDS_IDS_KEY) }
+ }
+ }
+
+ fun scanned(cardId: String) {
+ val restoredList = restore()
+ val foundItem = findCardInfo(cardId, restoredList)?.copy(isScanned = true)
+ ?: UsedCardInfo(cardId, true)
+
+ save(foundItem, restoredList)
+ }
+
+ fun wasScanned(cardId: String): Boolean {
+ return findCardInfo(cardId)?.isScanned ?: false
+ }
+
+ fun activationStarted(cardId: String) {
+ val restoredList = restore()
+ val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivationStarted = true)
+ ?: UsedCardInfo(cardId, isActivationStarted = true)
+
+ save(foundItem, restoredList)
+ }
+
+ fun activationIsStarted(cardId: String): Boolean {
+ return findCardInfo(cardId)?.isActivationStarted ?: false
+ }
+
+ fun activationFinished(cardId: String) {
+ val restoredList = restore()
+ val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivationStarted = false)
+ ?: UsedCardInfo(cardId, isActivationStarted = false)
+
+ save(foundItem, restoredList)
+ }
+
+ private fun findCardInfo(cardId: String, list: MutableList? = null): UsedCardInfo? {
+ val findInList = list ?: restore()
+ return findInList.firstOrNull { it.cardId == cardId }
+ }
+
+ private fun save(usedCardInfo: UsedCardInfo?, usedCardsInfo: MutableList) {
+ val info = usedCardInfo ?: return
+
+ usedCardsInfo.replaceByOrAdd(info) { it.cardId == info.cardId }
+ save(usedCardsInfo)
+ }
+
+ private fun save(list: MutableList): Boolean {
+ return try {
+ val json = jsonConverter.toJson(list)
+ preferences.edit { putString(USED_CARDS_INFO, json) }
+ true
+ } catch (ex: Exception) {
+ Timber.e(ex)
+ false
+ }
+ }
+
+ private fun restore(): MutableList {
+ val json = preferences.getString(USED_CARDS_INFO, null) ?: return mutableListOf()
+ return try {
+ jsonConverter.fromJson(json, jsonConverter.typedList(UsedCardInfo::class.java))!!
+ } catch (ex: Exception) {
+ preferences.edit(true) { remove(USED_CARDS_INFO) }
+ mutableListOf()
+ }
+ }
+
+ companion object {
+ private const val USED_CARDS_INFO = "usedCardsInfo"
+ private const val SCANNED_CARDS_IDS_KEY = "scannedCardIds"
+ }
+}
+
+private data class UsedCardInfo(
+ val cardId: String,
+ val isScanned: Boolean = false,
+ val isActivationStarted: Boolean = false,
+)
\ No newline at end of file
diff --git a/app/src/main/res/drawable/anim_progress_balance.xml b/app/src/main/res/drawable/anim_progress_balance.xml
new file mode 100644
index 0000000000..ada4670265
--- /dev/null
+++ b/app/src/main/res/drawable/anim_progress_balance.xml
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_half_transparent_overlay.xml b/app/src/main/res/drawable/bg_half_transparent_overlay.xml
new file mode 100644
index 0000000000..4b5937d793
--- /dev/null
+++ b/app/src/main/res/drawable/bg_half_transparent_overlay.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_activation_success.xml b/app/src/main/res/drawable/ic_activation_success.xml
new file mode 100644
index 0000000000..7957c82493
--- /dev/null
+++ b/app/src/main/res/drawable/ic_activation_success.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrow_refresh.xml b/app/src/main/res/drawable/ic_arrow_refresh.xml
new file mode 100644
index 0000000000..1acc645cec
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_refresh.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml b/app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml
new file mode 100644
index 0000000000..51b285c947
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_copy_green.xml b/app/src/main/res/drawable/ic_copy_green.xml
new file mode 100644
index 0000000000..a4145be642
--- /dev/null
+++ b/app/src/main/res/drawable/ic_copy_green.xml
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_home_back_card.xml b/app/src/main/res/drawable/ic_home_back_card.xml
new file mode 100644
index 0000000000..f15c47ba3f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_home_back_card.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_home_front_card.xml b/app/src/main/res/drawable/ic_home_front_card.xml
new file mode 100644
index 0000000000..19969e806e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_home_front_card.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_progress_balance.xml b/app/src/main/res/drawable/ic_progress_balance.xml
new file mode 100644
index 0000000000..d31defe1e7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_progress_balance.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_share_green.xml b/app/src/main/res/drawable/ic_share_green.xml
new file mode 100644
index 0000000000..57ee232b90
--- /dev/null
+++ b/app/src/main/res/drawable/ic_share_green.xml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/app/src/main/res/drawable/ripple.xml b/app/src/main/res/drawable/ripple.xml
new file mode 100644
index 0000000000..c6e07a265f
--- /dev/null
+++ b/app/src/main/res/drawable/ripple.xml
@@ -0,0 +1,32 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_circle.xml b/app/src/main/res/drawable/shape_circle.xml
index f857819f4d..59a662d141 100644
--- a/app/src/main/res/drawable/shape_circle.xml
+++ b/app/src/main/res/drawable/shape_circle.xml
@@ -1,6 +1,9 @@
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_rectangle_rounded_100.xml b/app/src/main/res/drawable/shape_rectangle_rounded_100.xml
new file mode 100644
index 0000000000..7f26fb8be5
--- /dev/null
+++ b/app/src/main/res/drawable/shape_rectangle_rounded_100.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_rectangle_rounded_8.xml b/app/src/main/res/drawable/shape_rectangle_rounded_8.xml
new file mode 100644
index 0000000000..a788f39093
--- /dev/null
+++ b/app/src/main/res/drawable/shape_rectangle_rounded_8.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_refresh_button.xml b/app/src/main/res/drawable/shape_refresh_button.xml
new file mode 100644
index 0000000000..ac9508438d
--- /dev/null
+++ b/app/src/main/res/drawable/shape_refresh_button.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml b/app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml
new file mode 100644
index 0000000000..a603013d47
--- /dev/null
+++ b/app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_onboarding_address_info.xml b/app/src/main/res/layout/dialog_onboarding_address_info.xml
new file mode 100644
index 0000000000..92c780e969
--- /dev/null
+++ b/app/src/main/res/layout/dialog_onboarding_address_info.xml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_wallet_trade.xml b/app/src/main/res/layout/dialog_wallet_trade.xml
new file mode 100644
index 0000000000..062d0286d1
--- /dev/null
+++ b/app/src/main/res/layout/dialog_wallet_trade.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_disclaimer.xml b/app/src/main/res/layout/fragment_disclaimer.xml
index 0c771b9995..bec0ceace0 100644
--- a/app/src/main/res/layout/fragment_disclaimer.xml
+++ b/app/src/main/res/layout/fragment_disclaimer.xml
@@ -28,17 +28,15 @@
android:id="@+id/cl_details_confirm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="33dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
+ app:layout_constraintBottom_toBottomOf="parent">
@@ -62,18 +60,27 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintGuide_percent="0.37" />
+
+
+ app:layout_constraintStart_toEndOf="@+id/guideline"
+ android:layout_marginBottom="24dp" />
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
deleted file mode 100644
index 33157a5fe6..0000000000
--- a/app/src/main/res/layout/fragment_home.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_onboarding_main.xml b/app/src/main/res/layout/fragment_onboarding_main.xml
new file mode 100644
index 0000000000..86c9138e69
--- /dev/null
+++ b/app/src/main/res/layout/fragment_onboarding_main.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_onboarding_wallet.xml b/app/src/main/res/layout/fragment_onboarding_wallet.xml
new file mode 100644
index 0000000000..77d9ef65f8
--- /dev/null
+++ b/app/src/main/res/layout/fragment_onboarding_wallet.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_topup.xml b/app/src/main/res/layout/fragment_topup.xml
index 8069c307f0..01182c0c4c 100644
--- a/app/src/main/res/layout/fragment_topup.xml
+++ b/app/src/main/res/layout/fragment_topup.xml
@@ -11,7 +11,7 @@
android:layout_height="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="@drawable/ic_close"
- app:title="@string/wallet_button_topup" />
+ app:title="@string/wallet_button_buy" />
@@ -61,39 +61,33 @@
android:src="@drawable/card_placeholder"
app:layout_constraintTop_toTopOf="parent" />
-
-
-
+ app:constraint_referenced_ids="iv_card,tv_twin_card_number" />
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/barrier_currency_title" />
@@ -93,13 +114,13 @@
app:layout_constraintTop_toBottomOf="@+id/l_wallet_details">
+ android:textColor="@android:color/white"
+ android:textSize="14sp" />
-
+
+
+
+
+ app:layout_constraintHorizontal_chainStyle="packed"
+ app:layout_constraintStart_toEndOf="@id/btn_top_up"
+ app:layout_constraintEnd_toStartOf="@id/btn_confirm"
+ app:layout_constraintTop_toBottomOf="@id/barrier"
+ app:layout_constraintVertical_bias="1" />
diff --git a/app/src/main/res/layout/item_currency_wallet.xml b/app/src/main/res/layout/item_currency_wallet.xml
index d4a2a6dfd2..3b74abdffd 100644
--- a/app/src/main/res/layout/item_currency_wallet.xml
+++ b/app/src/main/res/layout/item_currency_wallet.xml
@@ -17,7 +17,8 @@
+ android:layout_height="wrap_content"
+ android:minHeight="76dp">
+
+
+
+
diff --git a/app/src/main/res/layout/item_popular_token.xml b/app/src/main/res/layout/item_popular_token.xml
index 8d22ab57b4..a052651d61 100644
--- a/app/src/main/res/layout/item_popular_token.xml
+++ b/app/src/main/res/layout/item_popular_token.xml
@@ -3,7 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="52dp"
+ android:layout_height="wrap_content"
+ android:minHeight="52dp"
android:layout_marginBottom="1dp"
android:background="@android:color/white">
@@ -31,9 +32,16 @@
app:layout_constraintTop_toTopOf="@id/iv_currency"
tools:text="J" />
+
+
@@ -93,4 +104,12 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_onboarding_bottom_action_views.xml b/app/src/main/res/layout/layout_onboarding_bottom_action_views.xml
new file mode 100644
index 0000000000..54f8722018
--- /dev/null
+++ b/app/src/main/res/layout/layout_onboarding_bottom_action_views.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_onboarding_home.xml b/app/src/main/res/layout/layout_onboarding_home.xml
new file mode 100644
index 0000000000..6e4d367320
--- /dev/null
+++ b/app/src/main/res/layout/layout_onboarding_home.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_onboarding_note.xml b/app/src/main/res/layout/layout_onboarding_note.xml
new file mode 100644
index 0000000000..8393ecde9e
--- /dev/null
+++ b/app/src/main/res/layout/layout_onboarding_note.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_pseudo_toolbar.xml b/app/src/main/res/layout/layout_pseudo_toolbar.xml
new file mode 100644
index 0000000000..f9ae5fe2ab
--- /dev/null
+++ b/app/src/main/res/layout/layout_pseudo_toolbar.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_send_address_payid.xml b/app/src/main/res/layout/layout_send_address_payid.xml
index 00a5617a9d..2b0f046335 100644
--- a/app/src/main/res/layout/layout_send_address_payid.xml
+++ b/app/src/main/res/layout/layout_send_address_payid.xml
@@ -32,6 +32,7 @@
android:id="@+id/etAddressOrPayId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="@color/backgroundLightGray"
android:ellipsize="middle"
android:inputType="textNoSuggestions"
android:paddingStart="0dp"
@@ -150,6 +151,7 @@
android:id="@+id/etMemo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="@color/backgroundLightGray"
android:ellipsize="middle"
android:paddingStart="0dp"
android:paddingEnd="0dp"
@@ -180,6 +182,7 @@
android:id="@+id/etDestinationTag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="@color/backgroundLightGray"
android:ellipsize="end"
android:inputType="number"
android:paddingStart="0dp"
diff --git a/app/src/main/res/layout/layout_send_amount.xml b/app/src/main/res/layout/layout_send_amount.xml
index e2b5441fa2..b2e8700482 100644
--- a/app/src/main/res/layout/layout_send_amount.xml
+++ b/app/src/main/res/layout/layout_send_amount.xml
@@ -51,6 +51,7 @@
android:id="@+id/etAmountToSend"
android:layout_width="match_parent"
android:layout_height="82dp"
+ android:background="@color/backgroundLightGray"
android:fontFamily="sans-serif-light"
android:inputType="numberDecimal"
android:paddingStart="0dp"
diff --git a/app/src/main/res/layout/layout_token.xml b/app/src/main/res/layout/layout_token.xml
index 8e03d11ca1..fc9486f79c 100644
--- a/app/src/main/res/layout/layout_token.xml
+++ b/app/src/main/res/layout/layout_token.xml
@@ -20,7 +20,7 @@
android:id="@+id/v_token_background"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:background="@color/backgroundGray"
+ android:background="@color/backgroundLightGray"
app:layout_constraintTop_toBottomOf="@id/tv_token_title"
app:layout_constraintBottom_toBottomOf="@id/tv_token_fiat_amount"/>
diff --git a/app/src/main/res/layout/layout_wallet_short_buttons.xml b/app/src/main/res/layout/layout_wallet_short_buttons.xml
index 072306c7b7..be494ee95b 100644
--- a/app/src/main/res/layout/layout_wallet_short_buttons.xml
+++ b/app/src/main/res/layout/layout_wallet_short_buttons.xml
@@ -28,10 +28,10 @@
android:layout_marginStart="8dp"
android:layout_marginTop="30dp"
android:layout_marginBottom="33dp"
- android:drawableTop="@drawable/ic_arrow_up_short_btn"
+ android:drawableTop="@drawable/ic_arrows_up_down_short_btn"
android:gravity="bottom|center_horizontal"
android:paddingTop="1dp"
- android:text="@string/wallet_button_topup"
+ android:text="@string/wallet_button_trade"
app:drawableTint="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btn_confirm_short"
diff --git a/app/src/main/res/layout/layout_warning.xml b/app/src/main/res/layout/layout_warning.xml
index a9a76d4637..e2f6f41d6f 100644
--- a/app/src/main/res/layout/layout_warning.xml
+++ b/app/src/main/res/layout/layout_warning.xml
@@ -44,7 +44,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_marginTop="12dp"
+ android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:text="Test text messaging, or texting, is the act of composing and sending electronic messages, typically consisting of alphabetic and numeric characters, between"
diff --git a/app/src/main/res/layout/lp_onboarding_create_wallet.xml b/app/src/main/res/layout/lp_onboarding_create_wallet.xml
new file mode 100644
index 0000000000..fa4505f353
--- /dev/null
+++ b/app/src/main/res/layout/lp_onboarding_create_wallet.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/lp_onboarding_done.xml b/app/src/main/res/layout/lp_onboarding_done.xml
new file mode 100644
index 0000000000..c6294b65ca
--- /dev/null
+++ b/app/src/main/res/layout/lp_onboarding_done.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/lp_onboarding_done_activation.xml b/app/src/main/res/layout/lp_onboarding_done_activation.xml
new file mode 100644
index 0000000000..fab21fdfb2
--- /dev/null
+++ b/app/src/main/res/layout/lp_onboarding_done_activation.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml
new file mode 100644
index 0000000000..65705f3fce
--- /dev/null
+++ b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/test_leapfrog_fragment.xml b/app/src/main/res/layout/test_leapfrog_fragment.xml
new file mode 100644
index 0000000000..52b720ad27
--- /dev/null
+++ b/app/src/main/res/layout/test_leapfrog_fragment.xml
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/test_leapfrog_widget.xml b/app/src/main/res/layout/test_leapfrog_widget.xml
new file mode 100644
index 0000000000..3fbf992180
--- /dev/null
+++ b/app/src/main/res/layout/test_leapfrog_widget.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_confetti.xml b/app/src/main/res/layout/view_confetti.xml
new file mode 100644
index 0000000000..ad07b6e0bd
--- /dev/null
+++ b/app/src/main/res/layout/view_confetti.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_onboarding_progress.xml b/app/src/main/res/layout/view_onboarding_progress.xml
new file mode 100644
index 0000000000..415f8bd19c
--- /dev/null
+++ b/app/src/main/res/layout/view_onboarding_progress.xml
@@ -0,0 +1,10 @@
+
+
diff --git a/app/src/main/res/layout/view_onboarding_refresh_balance.xml b/app/src/main/res/layout/view_onboarding_refresh_balance.xml
new file mode 100644
index 0000000000..c9bf3aacb2
--- /dev/null
+++ b/app/src/main/res/layout/view_onboarding_refresh_balance.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_onboarding_tv_balance.xml b/app/src/main/res/layout/view_onboarding_tv_balance.xml
new file mode 100644
index 0000000000..5dd54d7067
--- /dev/null
+++ b/app/src/main/res/layout/view_onboarding_tv_balance.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/raw/anim_confetti.json b/app/src/main/res/raw/anim_confetti.json
new file mode 100644
index 0000000000..f2d1e7f974
--- /dev/null
+++ b/app/src/main/res/raw/anim_confetti.json
@@ -0,0 +1 @@
+{"v":"5.6.3","fr":30,"ip":0,"op":120,"w":1242,"h":2688,"nm":"Comp 1","ddd":1,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 14","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[692,1011.9999999999999,0],"ix":2},"a":{"a":0,"k":[71,-332,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-48,204],[72.555,109.763],[-9.41,53.993],[11.376,60.133],[-20.778,48.214],[-43.71,34.149],[-28,80]],"o":[[48,-204],[-46.308,-70.056],[6.44,-36.951],[-18.438,-97.46],[10.775,-25.002],[128,-100],[28,-80]],"v":[[71,-332],[151,-620],[209.484,-736.613],[141,-856],[199.096,-959.097],[193,-1114],[371,-1280]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14.077,"s":[0]},{"t":46.384765625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11.923,"s":[0]},{"t":42.0771484375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":2,"op":112,"st":2,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 13","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[648,1040,0],"ix":2},"a":{"a":0,"k":[27,-304,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[28,112],[154,-50],[122,52],[60,164]],"o":[[0,0],[-24,-96],[-150.386,48.827],[-141.68,-60.388],[-60,-164]],"v":[[27,-304],[-107,-530],[-249,-720],[-335,-908],[-537,-1284]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":35.615234375,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1,"s":[0]},{"t":27,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-5,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[674,1228,0],"ix":2},"a":{"a":0,"k":[53,-116,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-34,-98],[21,-81],[-20,-38],[-18,-48],[-8,-34],[-62,-38],[-24,-36],[-62,-30],[-84,-22]],"o":[[34,98],[-11.008,42.46],[12.721,24.17],[18,48],[9.222,39.192],[62,38],[24,36],[62,30],[84,22]],"v":[[53,-116],[134,25],[94,107],[181,157],[150,265],[237,323],[250,463],[383,540],[480,694]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254901960784,0.807843137254902,0.3607843137254902,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22.692,"s":[0]},{"t":55,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20.538,"s":[0]},{"t":50.6923828125,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":6,"op":112,"st":6,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[704,1180,0],"ix":2},"a":{"a":0,"k":[83,-164,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-38,-60],[-40.126,23.279],[42.5,-0.5],[-35,2.5],[-47.28,-26.863],[-20,-30],[-21.113,-24.13],[-34,-42],[-16,-40]],"o":[[38,60],[65.5,-38],[-20.024,0.236],[41.06,-2.933],[44,25],[20,30],[14,16],[34,42],[16,40]],"v":[[83,-164],[166.5,-75],[217.5,-41.5],[222.5,-81.5],[377,-77],[378,1],[458,17],[478,91],[616,178]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7.615,"s":[0]},{"t":39.9228515625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":31.3076171875,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-3,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 10","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[730,1100,0],"ix":2},"a":{"a":0,"k":[109,-244,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-72,-10],[-51,10],[13,-16],[-24.905,-7.729],[-10,10]],"o":[[72,10],[51,-10],[-10.168,12.514],[29,9],[10,-10]],"v":[[109,-244],[483,-178],[533,-129],[556,-83],[618,-93]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":16.23,"s":[0]},{"t":48.5380859375,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14.077,"s":[0]},{"t":44.23046875,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":3,"op":112,"st":3,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[678,1086,0],"ix":2},"a":{"a":0,"k":[57,-258,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-106,94],[-78,82],[-2,37],[-15,15],[-27.82,25.594],[-23,24],[-38.877,17.279],[-24,-14]],"o":[[106,-94],[78,-82],[2.405,-44.488],[18.028,-18.028],[25,-23],[19.594,-20.446],[36,-16],[24,14]],"v":[[57,-258],[204,-404],[337,-493],[346,-597],[426,-624],[454,-699],[542,-728],[611,-805]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7.615,"s":[0]},{"t":39.9228515625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":31.3076171875,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-3,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Shape Layer 8","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[640,1170,0],"ix":2},"a":{"a":0,"k":[19,-174,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[92,-22],[38,-38],[78,-8],[38,-10],[10,-20]],"o":[[-92,22],[-38,38],[-78,8],[-38,10],[-10,20]],"v":[[19,-174],[-115,-120],[-283,-46],[-423,-50],[-517,32]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7.615,"s":[0]},{"t":39.9228515625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5.462,"s":[0]},{"t":35.615234375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Shape Layer 7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[661,1191,0],"ix":2},"a":{"a":0,"k":[40,-153,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[16.398,-37.618],[29.992,-37.49],[41,-30],[60.195,-37.77],[80.591,1.465],[-31.635,-5.678],[109.189,24.816],[22,-30]],"o":[[-34,78],[-44,55],[-59.359,43.434],[-51,32],[-55,-1],[39,7],[-132,-30],[-22,30]],"v":[[40,-153],[-41,-58],[-104,84],[-253,140],[-359,228],[-381,175],[-459,204],[-652,321]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":18.385,"s":[0]},{"t":50.6923828125,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14.077,"s":[0]},{"t":42.0771484375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":2,"op":112,"st":2,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Shape Layer 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[646,1126,0],"ix":2},"a":{"a":0,"k":[25,-218,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[90,52],[20,-18],[44,46],[60,-36],[34,64],[-46,-10],[34,-50],[52,12],[28,10]],"o":[[-90,-52],[-20,18],[-44,-46],[-60,36],[-34,-64],[46,10],[-34,50],[-52,-12],[-28,-10]],"v":[[25,-218],[-175,-248],[-271,-286],[-379,-328],[-487,-310],[-535,-444],[-469,-396],[-547,-342],[-627,-366]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5.462,"s":[0]},{"t":37.76953125,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":35.615234375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":10,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":184,"ix":10},"p":{"a":0,"k":[633,1188,0],"ix":2},"a":{"a":0,"k":[12,-198,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":112,"st":0,"bm":0},{"ddd":1,"ind":11,"ty":4,"nm":"Shape Layer 41","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0.179,"s":[6.395,-188.007,0],"to":[3.99,-626.075,396.962],"ti":[-3.99,626.075,-396.962]},{"t":34.7802734375,"s":[30.337,-3944.458,2381.774]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":12,"ty":4,"nm":"Shape Layer 40","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":80.50390625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0.179,"s":[6.395,-188.007,0],"to":[172.235,-647.46,153.434],"ti":[-172.235,647.46,-153.434]},{"t":80,"s":[1039.806,-4072.764,920.605]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":13,"ty":4,"nm":"Shape Layer 39","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0.179,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0.179,"s":[6.395,-188.007,0],"to":[140.553,-166.199,-42.398],"ti":[-140.553,166.199,42.398]},{"t":15.0078125,"s":[849.715,-1185.203,-254.39]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":14,"ty":3,"nm":"Null 2","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-49,"ix":10},"p":{"a":0,"k":[621,1344,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":112,"st":-4,"bm":0},{"ddd":0,"ind":15,"ty":3,"nm":"Null 3","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":82,"ix":10},"p":{"a":0,"k":[621,1344,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":112,"st":-4,"bm":0},{"ddd":1,"ind":16,"ty":4,"nm":"Shape Layer 105","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":87.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[727.321,51.433,435.455],"ti":[-727.321,-51.433,-435.455]},{"t":34.544921875,"s":[4250.962,186.167,2612.729]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":17,"ty":4,"nm":"Shape Layer 104","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":80.50390625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[527.848,55.985,153.434],"ti":[-527.848,-55.985,-153.434]},{"t":54.552734375,"s":[3054.124,213.477,920.605]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":18,"ty":4,"nm":"Shape Layer 103","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[4.145,138.496,-42.398],"ti":[-4.145,-138.496,42.398]},{"t":15.0078125,"s":[-88.093,708.543,-254.39]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":19,"ty":4,"nm":"Shape Layer 102","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":-0.179,"s":[-112.965,-122.431,0],"to":[-61.615,187.567,26.907],"ti":[61.615,-187.567,-26.907]},{"t":31.0732421875,"s":[-482.654,1002.974,161.442]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":20,"ty":4,"nm":"Shape Layer 101","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":84.2109375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.824},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-90.863,90.863,0],"ti":[88.083,-357.903,-768.642]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.164},"t":17.479,"s":[-658.144,422.748,0],"to":[-84.157,341.951,734.383],"ti":[-714.89,-84.078,-197.259]},{"t":86.6826171875,"s":[3631.196,927.217,1183.551]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":21,"ty":4,"nm":"Shape Layer 100","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.796},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-134.704,42.073,0],"ti":[7.203,-205.415,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.157},"t":28,"s":[-921.188,130.006,0],"to":[-19.567,558.035,0],"ti":[-540.328,-229.432,0]},{"t":116,"s":[2320.778,1506.598,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":22,"ty":4,"nm":"Shape Layer 99","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.775},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-115.257,9.245,0],"ti":[52.068,-163.912,-53.249]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.155},"t":19.951,"s":[-804.504,-66.963,0],"to":[-48.777,153.551,49.883],"ti":[-156.371,-109.917,-25.485]},{"t":91,"s":[2733.865,1051.661,319.492]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":23,"ty":4,"nm":"Shape Layer 98","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.855},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-138.996,-53.461,0],"ti":[-421.763,15.031,16.673]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.174},"t":21.187,"s":[-946.942,-443.198,0],"to":[421.763,-15.031,-16.673],"ti":[-560.76,-38.43,16.673]},{"t":100.2763671875,"s":[2417.616,-212.616,-100.037]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":24,"ty":4,"nm":"Shape Layer 97","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[59]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.673},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-87.16,25.736,-45.544],"ti":[-273.542,-218.754,122.377]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.14},"t":31.073,"s":[-660.653,26.246,-273.265],"to":[129.994,103.957,-58.156],"ti":[-320.287,-255.677,-112.981]},{"t":111.3984375,"s":[3683.402,989.215,1150.251]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[3.444,9.757]],"o":[[0,0],[0,0],[0,0],[-4.941,-14]],"v":[[72,-317],[4,-279],[24.151,-217.936],[88.307,-269.617]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":25,"ty":4,"nm":"Shape Layer 96","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.853},"o":{"x":0.167,"y":0.167},"t":0,"s":[-89.038,-0.153,0],"to":[-552.568,-84.135,-9.152],"ti":[-1514.035,-462.134,43.281]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.183},"t":38,"s":[95.207,-392.324,-54.91],"to":[684.779,209.018,-19.576],"ti":[-315.276,-115.122,160.297]},{"t":97,"s":[1986.864,298.408,-1016.689]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":26,"ty":4,"nm":"Shape Layer 95","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-14,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.774},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-110.347,-144.949,-65.532],"ti":[-365.662,-268.839,-214.28]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.147},"t":26,"s":[-799.777,-997.863,-393.192],"to":[365.662,268.839,214.28],"ti":[-476.009,-413.788,-279.812]},{"t":76.796875,"s":[2056.275,1484.866,1285.683]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":27,"ty":4,"nm":"Shape Layer 94","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-90.481,-247.593,46.629],"ti":[90.481,247.593,-46.629]},{"t":44,"s":[-680.583,-1613.73,279.773]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":28,"ty":4,"nm":"Shape Layer 93","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[69.003,-110.281,-16.168],"ti":[-69.003,110.281,16.168]},{"t":32,"s":[276.323,-789.855,-97.005]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":29,"ty":4,"nm":"Shape Layer 92","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-10,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[305.417,119.743,41.862],"ti":[-305.417,-119.743,-41.862]},{"t":27,"s":[1694.805,590.289,251.17]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":30,"ty":4,"nm":"Shape Layer 91","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[318.521,75.426,-43.518],"ti":[-318.521,-75.426,43.518]},{"t":24,"s":[1773.433,324.385,-261.107]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":31,"ty":4,"nm":"Shape Layer 90","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":89.154296875,"s":[1800]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[77.354,134.863,-215.584],"ti":[-77.354,-134.863,215.584]},{"t":27.3662109375,"s":[326.43,681.005,-1293.506]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":32,"ty":4,"nm":"Shape Layer 89","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[301.698,164.851,-47.459],"ti":[-301.698,-164.851,47.459]},{"t":103.9833984375,"s":[1697.223,866.674,-284.755]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[84.659,-333.362],[4,-279],[45.665,-243.617],[119.735,-297.935]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":33,"ty":4,"nm":"Shape Layer 88","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[159.503,223.195,-0.572],"ti":[-159.503,-223.195,0.572]},{"t":37.251953125,"s":[844.051,1216.741,-3.43]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":34,"ty":4,"nm":"Shape Layer 87","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[33.76,196.346,-7.096],"ti":[-33.76,-196.346,7.096]},{"t":40.958984375,"s":[89.594,1055.648,-42.576]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":35,"ty":4,"nm":"Shape Layer 86","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-16.649,163.663,0],"ti":[16.649,-163.663,0]},{"t":24.89453125,"s":[-212.861,859.547,0]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":36,"ty":4,"nm":"Shape Layer 85","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-71.747,128.05,0],"ti":[71.747,-128.05,0]},{"t":49.609375,"s":[-543.449,645.87,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":37,"ty":4,"nm":"Shape Layer 84","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":112.6337890625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.728},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-81.837,27.324,33.544],"ti":[-153.275,-262.436,-33.544]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.131},"t":21,"s":[-603.986,41.512,201.262],"to":[153.275,262.436,33.544],"ti":[-235.112,-235.112,0]},{"t":50.845703125,"s":[806.688,1452.185,201.262]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":38,"ty":4,"nm":"Shape Layer 83","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-6,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.652},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-171.831,20.57,3.486],"ti":[602.026,-193.886,-848.553]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.142},"t":35,"s":[-1143.953,0.988,20.917],"to":[-778.519,946.911,884.934],"ti":[-1189.71,-488.979,-884.978]},{"t":139,"s":[5994.306,2934.863,5330.786]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":39,"ty":4,"nm":"Shape Layer 82","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-23,"s":[0]},{"t":97.8046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.883},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-120.682,-96.313,17.582],"ti":[57.484,-266.769,15.802]},{"i":{"x":0.833,"y":0.817},"o":{"x":0.167,"y":0.198},"t":15.008,"s":[-837.059,-700.31,105.493],"to":[-25.433,118.027,-6.991],"ti":[-511.749,-461.825,46.841]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.143},"t":54.553,"s":[271.713,386.113,403.89],"to":[308.693,278.579,-28.255],"ti":[-142.63,-100.689,10.833]},{"t":76.796875,"s":[1060.877,1065.009,-67.253]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":40,"ty":4,"nm":"Shape Layer 81","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-12,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[135.751,-389.001,217.62],"ti":[-135.751,389.001,-217.62]},{"t":27,"s":[676.808,-2462.175,1305.721]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":41,"ty":4,"nm":"Shape Layer 80","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-25,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.849},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-70.649,26.407,0],"ti":[-291.085,-122.356,90.248]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.171},"t":16,"s":[-561.59,30.269,0],"to":[291.085,122.356,-90.248],"ti":[-361.735,-95.95,90.248]},{"t":82.9755859375,"s":[1608.818,605.966,-541.488]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":42,"ty":4,"nm":"Shape Layer 79","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.704},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-49.159,-63.557,4.223],"ti":[-0.005,148.838,-20.562]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.144},"t":24,"s":[-432.65,-509.511,25.34],"to":[0.017,-477.286,65.936],"ti":[-415.767,197.78,-61.713]},{"t":91.6259765625,"s":[2061.954,-1696.192,395.618]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":43,"ty":4,"nm":"Shape Layer 78","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-20,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[33.977,-166.361,22.596],"ti":[-33.977,166.361,-22.596]},{"t":30,"s":[66.167,-1126.338,135.575]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":44,"ty":4,"nm":"Shape Layer 77","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-16,"s":[0]},{"t":99.041015625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[345.992,-256.663,189.679],"ti":[-345.992,256.663,-189.679]},{"t":27,"s":[1938.258,-1668.151,1138.074]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":45,"ty":4,"nm":"Shape Layer 76","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":45.90234375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":105.2197265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.826},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[120.875,-60.552,0],"ti":[-340.061,254.939,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.163},"t":27.366,"s":[587.554,-491.48,0],"to":[340.061,-254.939,0],"ti":[-219.187,194.387,0]},{"t":82.9755859375,"s":[1902.674,-1657.802,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":46,"ty":4,"nm":"Shape Layer 75","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-18,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.926},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[208.471,-17.277,73.2],"ti":[-257.939,74.184,269.358]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.228},"t":15.897,"s":[1113.129,-231.834,439.203],"to":[257.939,-74.184,-269.358],"ti":[-49.469,56.907,342.559]},{"t":90,"s":[1409.942,-573.277,-1616.149]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":47,"ty":4,"nm":"Shape Layer 74","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-24,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[308.185,154.066,17.938],"ti":[-308.185,-154.066,-17.938]},{"t":24.89453125,"s":[1711.415,796.226,107.63]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":48,"ty":4,"nm":"Shape Layer 73","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-47.653,624.272,396.962],"ti":[47.653,-624.272,-396.962]},{"t":34.7802734375,"s":[-267.632,3580.053,2381.774]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":49,"ty":4,"nm":"Shape Layer 72","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":80.50390625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-143.363,403.815,153.434],"ti":[143.363,-403.815,-153.434]},{"t":54.552734375,"s":[-841.892,2257.314,920.605]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":50,"ty":4,"nm":"Shape Layer 71","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-196.222,199.787,-42.398],"ti":[196.222,-199.787,42.398]},{"t":32.30859375,"s":[-1159.042,1033.147,-254.39]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":51,"ty":4,"nm":"Shape Layer 70","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.868},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-181.982,76.554,26.907],"ti":[353.39,-64.568,-26.907]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.207},"t":31.073,"s":[-1073.604,293.746,161.442],"to":[-353.39,64.568,26.907],"ti":[171.408,11.986,0]},{"t":73.08984375,"s":[-2102.054,221.83,161.442]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":52,"ty":4,"nm":"Shape Layer 69","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":84.2109375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-128.187,-8.964,0],"ti":[128.187,8.964,0]},{"t":33,"s":[-750.834,-219.36,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":53,"ty":4,"nm":"Shape Layer 68","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-120.126,-74.06,0],"ti":[120.126,74.06,0]},{"t":39,"s":[-702.471,-609.938,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":54,"ty":4,"nm":"Shape Layer 67","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.723},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-82.592,-80.92,0],"ti":[157.866,-68.24,-53.249]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.139},"t":27,"s":[-477.265,-651.099,0],"to":[-147.887,63.926,49.883],"ti":[-19.633,-190.127,-25.485]},{"t":80.50390625,"s":[-147.831,2175.133,319.492]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":55,"ty":4,"nm":"Shape Layer 66","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.749},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-50.842,-139.975,0],"ti":[92.088,-447.218,16.673]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.14},"t":36,"s":[-286.764,-1005.43,0],"to":[-92.088,447.218,-16.673],"ti":[41.246,-587.193,16.673]},{"t":100.2763671875,"s":[-534.238,2517.728,-100.037]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":56,"ty":4,"nm":"Shape Layer 65","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[59]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.767},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[-6.333,-132.667,0],"ti":[83.687,200.398,-334.198]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.153},"t":26,"s":[-31.605,-984.007,0],"to":[-63.434,-151.901,253.321],"ti":[-17.165,-409.463,-112.981]},{"t":111.3984375,"s":[283.153,3184.715,1150.251]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[3.444,9.757]],"o":[[0,0],[0,0],[0,0],[-4.941,-14]],"v":[[72,-317],[4,-279],[24.151,-217.936],[88.307,-269.617]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.671,0.345,0.345,0.5,0.655,0.247,0.461,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":57,"ty":4,"nm":"Shape Layer 64","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.836},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[64.5,-169.594,-9.152],"ti":[-113.79,-117.061,113.731]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.168},"t":27,"s":[393.395,-1205.571,-54.91],"to":[155.887,160.368,-155.806],"ti":[28,-314.6,160.297]},{"t":79,"s":[225.395,682.029,-1016.689]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":58,"ty":4,"nm":"Shape Layer 63","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-14,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.787},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[37,-178.375,-65.532],"ti":[-37,-452.343,-214.28]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.151},"t":28,"s":[228.395,-1258.256,-393.192],"to":[37,452.343,214.28],"ti":[0,-630.718,-279.812]},{"t":88,"s":[228.395,2526.051,1285.683]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":59,"ty":4,"nm":"Shape Layer 62","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[127.5,-230.723,46.629],"ti":[-127.5,230.723,-46.629]},{"t":38,"s":[771.395,-1572.344,279.773]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":60,"ty":4,"nm":"Shape Layer 61","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[259.979,-20.273,-16.168],"ti":[-259.979,20.273,16.168]},{"t":19,"s":[1566.267,-309.648,-97.005]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":61,"ty":4,"nm":"Shape Layer 60","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-10,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[110,309.06,41.862],"ti":[-110,-309.06,-41.862]},{"t":31,"s":[666.395,1666.35,251.17]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":62,"ty":4,"nm":"Shape Layer 59","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[111,205.897,-43.518],"ti":[-111,-205.897,43.518]},{"t":33,"s":[672.395,1047.376,-261.107]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":63,"ty":4,"nm":"Shape Layer 58","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":89.154296875,"s":[1800]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[14.845,244.879,-215.584],"ti":[-14.845,-244.879,215.584]},{"t":27.3662109375,"s":[95.463,1281.264,-1293.506]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":64,"ty":4,"nm":"Shape Layer 57","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[44.909,224.342,-47.459],"ti":[-44.909,-224.342,47.459]},{"t":58.259765625,"s":[287.74,1180.472,-284.755]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[84.659,-333.362],[4,-279],[45.665,-243.617],[119.735,-297.935]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":65,"ty":4,"nm":"Shape Layer 56","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-63.805,266.808,-0.572],"ti":[63.805,-266.808,0.572]},{"t":51,"s":[-364.539,1435.268,-3.43]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":66,"ty":4,"nm":"Shape Layer 55","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.877},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-126.036,154.294,-7.096],"ti":[142.905,-395.535,7.096]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.203},"t":39,"s":[-737.928,760.184,-42.576],"to":[-142.905,395.535,-7.096],"ti":[16.869,-241.241,0]},{"t":117,"s":[-839.144,2207.633,-42.576]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":67,"ty":4,"nm":"Shape Layer 54","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-233.472,311.697,-17.966],"ti":[233.472,-311.697,17.966]},{"t":24,"s":[-1382.547,1704.606,-107.798]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":68,"ty":4,"nm":"Shape Layer 53","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.762},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-130.292,21.146,0],"ti":[485.802,-51.233,12.363]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.14},"t":22,"s":[-763.467,-38.702,0],"to":[-485.802,51.233,-12.363],"ti":[355.509,-30.087,12.363]},{"t":57,"s":[-2896.522,141.82,-74.176]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":69,"ty":4,"nm":"Shape Layer 52","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":112.6337890625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.78},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-74.311,-43.837,33.544],"ti":[97.505,-287.852,-33.544]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.149},"t":34,"s":[-427.58,-428.6,201.262],"to":[-97.505,287.852,33.544],"ti":[23.194,-331.689,0]},{"t":102,"s":[-566.743,1561.534,201.262]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":70,"ty":4,"nm":"Shape Layer 51","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-6,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-124.762,-149.834,3.486],"ti":[124.762,149.834,-3.486]},{"t":26,"s":[-730.286,-1064.579,20.917]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":71,"ty":4,"nm":"Shape Layer 50","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-23,"s":[0]},{"t":97.8046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.77},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-6.486,-154.267,17.582],"ti":[239.046,-131.632,15.802]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.142},"t":30,"s":[-20.629,-1091.18,105.493],"to":[-105.762,58.238,-6.991],"ti":[-17.583,-173.702,10.833]},{"t":76.796875,"s":[-107.774,1499.363,-67.253]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":72,"ty":4,"nm":"Shape Layer 49","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-12,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[382.643,-152.755,217.62],"ti":[-382.643,152.755,-217.62]},{"t":33,"s":[2302.253,-1104.538,1305.721]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":73,"ty":4,"nm":"Shape Layer 48","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-25,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.831},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[64.5,-93.5,0],"ti":[-64.5,-194.5,90.248]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"t":25,"s":[393.395,-749.007,0],"to":[64.5,194.5,-90.248],"ti":[0,-288,90.248]},{"t":82.9755859375,"s":[393.395,978.993,-541.488]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":74,"ty":4,"nm":"Shape Layer 47","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.885},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[65.626,-110,4.223],"ti":[-147.468,-10.238,-26.905]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.19},"t":20,"s":[400.149,-848.007,25.34],"to":[361.407,25.09,65.936],"ti":[-121.292,-241.908,-61.713]},{"t":91.6259765625,"s":[1127.898,603.439,395.618]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":75,"ty":4,"nm":"Shape Layer 46","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-20,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.816},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[147.845,-83.5,22.596],"ti":[-263.557,-41.975,24.609]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.159},"t":24,"s":[893.467,-689.007,135.575],"to":[408.719,65.094,-38.164],"ti":[88.693,-68.281,27.128]},{"t":74,"s":[-447.485,1243.193,-405.659]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":76,"ty":4,"nm":"Shape Layer 45","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-16,"s":[0]},{"t":99.041015625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.875},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[217.737,24,189.679],"ti":[-563.841,-88.203,-189.679]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.201},"t":26,"s":[1312.816,-44.007,1138.074],"to":[563.841,88.203,189.679],"ti":[-346.104,-64.203,0]},{"t":76.796875,"s":[3389.44,341.208,1138.074]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":77,"ty":4,"nm":"Shape Layer 44","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":45.90234375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":105.2197265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.866},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[125,51.5,0],"ti":[-125,-69.882,151.388]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.196},"t":27.366,"s":[756.395,120.993,0],"to":[125,69.882,-151.388],"ti":[0,-18.382,151.388]},{"t":72,"s":[756.395,231.284,-908.328]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":78,"ty":4,"nm":"Shape Layer 43","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-18,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[149.808,146,73.2],"ti":[-149.808,-146,-73.2]},{"t":26,"s":[905.246,687.993,439.203]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":79,"ty":4,"nm":"Shape Layer 42","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-24,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[85.912,333.667,17.938],"ti":[-85.912,-333.667,-17.938]},{"t":24.89453125,"s":[521.87,1813.993,107.63]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":80,"ty":4,"nm":"Shape Layer 38","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[176.199,-89.062,26.907],"ti":[-176.199,89.062,-26.907]},{"t":31.0732421875,"s":[1063.587,-722.379,161.442]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":81,"ty":4,"nm":"Shape Layer 37","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":84.2109375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[128.5,0,0],"ti":[-128.5,0,0]},{"t":30,"s":[777.395,-188.007,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":82,"ty":4,"nm":"Shape Layer 36","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[125,65.5,0],"ti":[-125,-65.5,0]},{"t":22,"s":[756.395,204.993,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":83,"ty":4,"nm":"Shape Layer 35","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.782},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[88.036,74.962,0],"ti":[-235.276,83.183,-29.696]},{"i":{"x":0.833,"y":0.693},"o":{"x":0.167,"y":0.155},"t":30,"s":[534.609,261.763,0],"to":[283.126,-100.101,35.736],"ti":[32.848,188.294,-25.485]},{"t":123,"s":[-62.525,-2846.235,319.492]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":84,"ty":4,"nm":"Shape Layer 33","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":454.311,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[59]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.737},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[42.167,-122.667,0],"ti":[-345.153,74.903,-198.64]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.151},"t":28,"s":[880.395,419.993,0],"to":[432.939,-93.953,249.162],"ti":[-17.165,-409.463,-112.981]},{"t":127,"s":[1064.167,5582.141,1150.251]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[3.444,9.757]],"o":[[0,0],[0,0],[0,0],[-4.941,-14]],"v":[[72,-317],[4,-279],[24.151,-217.936],[88.307,-269.617]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":85,"ty":4,"nm":"Shape Layer 32","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.841},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[21.5,-126.594,-9.152],"ti":[-109.46,-148.367,74.781]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.169},"t":25,"s":[756.395,396.429,-54.90999999999999],"to":[113.887,154.368,-77.806],"ti":[7.464,-520.935,-7.703]},{"t":124,"s":[711.613,3522.04,-8.689]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":86,"ty":4,"nm":"Shape Layer 31","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-14,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.843},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[37,-178.375,-65.532],"ti":[-48.635,-750.211,-214.28]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.169},"t":29,"s":[849.395,85.744,-393.192],"to":[48.635,750.211,214.28],"ti":[-11.635,-928.586,-279.812]},{"t":145,"s":[919.2079999999999,5657.262,1285.683]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":87,"ty":4,"nm":"Shape Layer 30","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[127.5,-230.723,46.629],"ti":[-127.5,230.723,-46.629]},{"t":25,"s":[1392.395,-228.34400000000002,279.773]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":88,"ty":4,"nm":"Shape Layer 29","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[128.5,-20.273,-16.168],"ti":[-128.5,20.273,16.168]},{"t":44,"s":[1398.395,1034.352,-97.005]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":89,"ty":4,"nm":"Shape Layer 28","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-10,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[110,309.06,41.862],"ti":[-110,-309.06,-41.862]},{"t":41,"s":[1287.395,3010.35,251.17]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":90,"ty":4,"nm":"Shape Layer 27","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[111,205.897,-43.518],"ti":[-111,-205.897,43.518]},{"t":31,"s":[1293.395,2391.376,-261.107]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":91,"ty":4,"nm":"Shape Layer 26","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":89.154296875,"s":[1800]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[1,335.659,-215.584],"ti":[-1,-335.659,215.584]},{"t":58,"s":[633.395,3169.948,-1293.506]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":92,"ty":4,"nm":"Shape Layer 25","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[-147.439,-471.248,-47.459],"ti":[147.439,471.248,47.459]},{"t":137.349609375,"s":[-878.24,-3015.497,-284.755]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[84.659,-333.362],[4,-279],[45.665,-243.617],[119.735,-297.935]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":93,"ty":4,"nm":"Shape Layer 24","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[53.803,-474.312,-0.572],"ti":[-53.803,474.312,0.572]},{"t":71,"s":[329.216,-3033.882,-3.43]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":94,"ty":4,"nm":"Shape Layer 23","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[436.949,-283.973,0],"ti":[-436.949,283.973,0]},{"t":71,"s":[2628.09,-1891.843,0]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":95,"ty":4,"nm":"Shape Layer 22","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[127.5,-103.954,0],"ti":[-127.5,103.954,0]},{"t":34,"s":[771.395,-811.733,0]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":96,"ty":4,"nm":"Shape Layer 21","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[128.5,-30.183,0],"ti":[-128.5,30.183,0]},{"t":26,"s":[777.395,-369.106,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":97,"ty":4,"nm":"Shape Layer 20","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":112.6337890625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.716},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[77.188,38.547,33.544],"ti":[-71.564,470.424,-33.544]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.136},"t":35,"s":[469.524,43.272,201.262],"to":[71.564,-470.424,33.544],"ti":[5.624,508.971,0]},{"t":100,"s":[435.782,-3010.554,201.262]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":98,"ty":4,"nm":"Shape Layer 19","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-6,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[134.91,140.766,3.486],"ti":[-134.91,-140.766,-3.486]},{"t":21,"s":[815.857,656.587,20.917]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":99,"ty":4,"nm":"Shape Layer 18","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-23,"s":[0]},{"t":97.8046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.828},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[17.232,153.439,17.582],"ti":[-229.282,147.986,15.802]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.165},"t":21,"s":[109.785,732.626,105.493],"to":[101.442,-65.474,-6.991],"ti":[29.657,172.052,10.833]},{"t":76.796875,"s":[76.884,-1982.01,-729.705]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":100,"ty":4,"nm":"Shape Layer 17","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-12,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[382.643,-152.755,217.62],"ti":[-382.643,152.755,-217.62]},{"t":25,"s":[2923.253,239.462,1305.721]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":101,"ty":4,"nm":"Shape Layer 16","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-25,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.829},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[64.5,-93.5,0],"ti":[-78.679,-339.952,90.248]},{"i":{"x":0.833,"y":0.714},"o":{"x":0.167,"y":0.165},"t":30,"s":[1014.395,594.993,0],"to":[78.679,339.952,-90.248],"ti":[-14.179,-433.452,90.248]},{"t":121,"s":[1099.471,3195.705,-541.488]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":102,"ty":4,"nm":"Shape Layer 15","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.822]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":102,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.828},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[65.626,-110,4.223],"ti":[-147.468,-10.238,-26.905]},{"i":{"x":0.833,"y":0.887},"o":{"x":0.167,"y":0.163},"t":28,"s":[1021.149,495.993,25.34],"to":[361.407,25.09,65.936],"ti":[-126.131,-151.254,-61.713]},{"t":71,"s":[1777.9360000000001,1403.514,395.618]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":103,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-20,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[147.845,-83.5,22.596],"ti":[-147.845,83.5,-22.596]},{"t":19,"s":[1514.467,654.993,135.575]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":104,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-16,"s":[0]},{"t":99.041015625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[393.671,83.751,189.679],"ti":[-393.671,-83.751,-189.679]},{"t":58,"s":[2989.419,1658.499,1138.074]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":105,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":45.90234375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":105.2197265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[249,109.5,0],"ti":[-249,-109.5,0]},{"t":68,"s":[2121.395,1812.9929999999997,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":106,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-18,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[499.008,399.431,73.2],"ti":[-499.008,-399.431,-73.2]},{"t":96,"s":[3621.4449999999997,3552.5799999999995,439.20300000000003]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":107,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-24,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[535.305,103.127,17.938],"ti":[-535.305,-103.127,-17.938]},{"t":82,"s":[3839.226,1774.756,107.63000000000001]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":108,"ty":1,"nm":"Medium Gray-Blue Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[621,1344,0],"ix":2},"a":{"a":0,"k":[621,1344,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":1242,"sh":2688,"sc":"#272d41","ip":112,"op":112,"st":0,"bm":0,"hidden":0}],"markers":[]}
diff --git a/app/src/main/res/values-h680dp/dimens.xml b/app/src/main/res/values-h680dp/dimens.xml
new file mode 100644
index 0000000000..b8c8d265ed
--- /dev/null
+++ b/app/src/main/res/values-h680dp/dimens.xml
@@ -0,0 +1,9 @@
+
+
+ -10dp
+ 0dp
+ 16dp
+ 106dp
+ 67dp
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index fc5a1fd7b2..5d0fe290b1 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -5,6 +5,8 @@
#0022D4
#1ACE80
+ @color/backgroundLightGray
+
@color/accent
#661ACE80
@color/darkGray6
@@ -14,6 +16,7 @@
#C4291C
#FFB71B
+ #FFFFFF
#8E8E93
#636366
#48484A
@@ -21,7 +24,7 @@
#2C2C2E
#1C1C1E
- #F9F9FA
+ #FFFFFF
#F9F9F9
#F4F5F6
#DE000000
@@ -29,10 +32,13 @@
#14181D
+ #F3F3F3
#F8F8FB
+ #DADADF
+ #D0D0D5
#C7C7CC
#D1D1D6
- #F2F2F7
+ #C9C9CD
#1F000000
#14212121
@@ -43,6 +49,8 @@
#CBE4FF
#E0E6FA
+ #55000000
+
#1C1C1E
#FFB71B
#CA0F03
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index fd2c18a380..496ccebf23 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -35,4 +35,10 @@
32sp
16dp
+ -30dp
+ -20dp
+ 8dp
+ 70dp
+ 37dp
+
diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml
index 0ff017a1aa..17edb1b769 100644
--- a/app/src/main/res/values/strings_untranslated.xml
+++ b/app/src/main/res/values/strings_untranslated.xml
@@ -1,5 +1,9 @@
- Top Up
+ Scan card failed. Try again
+
+ Buy
+ Sell
+ Trade
Default
Compatibility
Legacy
@@ -93,7 +97,9 @@ this wallet.
Manage tokens
Blockchains
- Ethereum Tokens
+ Ethereum tokens
+ Binance Smart Chain tokens
+ Binance Chain tokens
Copy
Share
+ Add tokens
@@ -156,6 +162,37 @@ this wallet.
This card can’t be used to establish WalletConnect session
The operation couldn\'t be completed
The operation couldn\'t be completed. \n\nYou have already established a WalletConnect session with this parameters.
+ Failed to establish WalletConnect session: timeout error. Please, try again later.
+
+ This is a Testnet card. Don\'t accept it as a payment. This card must only be used for testing and development purposes.
+
+
+ Do you want to buy or sell crypto?
Oops, the current version of the application is not ready to work with this card, please check for updates.
+
+
+ Welcome to Tangem
+ The safest way to buy, use and\n store cryptocurrency
+ Get a new card
+
+ Activating card
+ What does it mean?
+ Create a wallet
+ Let’s generate all the keys on your card and create a secure wallet
+ Create wallet
+
+ Top up your wallet
+ To get started, simply top up the card with any amount
+ Buy crypto
+ Show the wallet’s address
+ Scan the address to top up your wallet
+
+ Done!
+ Your crypto card is activated and ready to be used
+ Continue
+ Balance
+
+ Internal error: can\'t create wallet manager
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index ae260a7854..f008a00608 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -5,9 +5,11 @@
- @color/colorPrimary
- @color/colorSecondary
- @color/darkGray6
- - true
- @color/backgroundLightGray
- - false
+ - false
+ - true
+ - true
+ - @null
@@ -24,22 +26,45 @@
- #e34b1c
-
+
+
+
+
+
+
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index f2dcf4b2fe..0ac373259b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,9 +10,8 @@ buildscript {
classpath "com.android.tools.build:gradle:${versions.build_gradle}"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
classpath "com.github.dcendents:android-maven-gradle-plugin:2.1"
- classpath 'com.google.gms:google-services:4.3.5'
- classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
-// classpath 'com.google.firebase:perf-plugin:1.3.1'
+ classpath 'com.google.gms:google-services:4.3.8'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1'
}
}
diff --git a/dependencies.gradle b/dependencies.gradle
index 32b0ea5280..43b263845b 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -1,4 +1,4 @@
ext.versions = [
- kotlin : '1.5.10',
- build_gradle: '4.2.1',
+ kotlin : '1.5.30',
+ build_gradle: '7.0.2',
]
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index f987e8eb91..06543032c9 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -46,7 +46,7 @@ platform :android do
'versionName' => options[:versionName],
})
gradle(
- task: "assemble",
+ task: "bundle",
build_type: "Release",
properties: {
'versionCode' => options[:versionCode],
diff --git a/fastlane/README.md b/fastlane/README.md
index 7ef939cccf..5a974e90ae 100644
--- a/fastlane/README.md
+++ b/fastlane/README.md
@@ -12,7 +12,7 @@ Install _fastlane_ using
```
[sudo] gem install fastlane -NV
```
-or alternatively using `brew cask install fastlane`
+or alternatively using `brew install fastlane`
# Available Actions
## Android
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index c6dab66ff6..8e5b6ad562 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip