Updated on 2026-08-14
This commit is contained in:
commit
f823a41b4b
104 changed files with 19896 additions and 9976 deletions
|
|
@ -77,9 +77,9 @@ dependencies {
|
|||
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-52'
|
||||
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-115'
|
||||
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-115'
|
||||
implementation 'com.tangem:blockchain:develop-64'
|
||||
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-131'
|
||||
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-131'
|
||||
|
||||
// WebView
|
||||
implementation "androidx.browser:browser:1.3.0"
|
||||
|
|
@ -110,6 +110,9 @@ dependencies {
|
|||
implementation 'com.google.firebase:firebase-config-ktx'
|
||||
implementation 'com.google.firebase:firebase-analytics-ktx'
|
||||
|
||||
// AppsFlyer
|
||||
implementation 'com.appsflyer:af-android-sdk:6.5.1'
|
||||
|
||||
// WalletConnect
|
||||
implementation 'com.github.salomonbrys.kotson:kotson:2.5.0'
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
|
|
@ -35,7 +36,8 @@
|
|||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
tools:ignore="GoogleAppIndexingWarning"
|
||||
tools:replace="android:fullBackupContent">
|
||||
|
||||
|
||||
<activity
|
||||
|
|
|
|||
863
app/src/main/assets/avalanche_tokens.json
Normal file
863
app/src/main/assets/avalanche_tokens.json
Normal file
|
|
@ -0,0 +1,863 @@
|
|||
[
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/ZRX/logo.png",
|
||||
"symbol": "ZRX",
|
||||
"name": "0x",
|
||||
"contractAddress": "0x596fa47043f99a4e0f122243b841e55375cde0d2"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/1INCH/logo.png",
|
||||
"symbol": "1INCH",
|
||||
"name": "1inch",
|
||||
"contractAddress": "0xd501281565bf7789224523144fe5d98e8b28f267"
|
||||
},
|
||||
{
|
||||
"decimalCount": 8,
|
||||
"customIconUrl": "https://github.com/blocknetdx/documentation/blob/master/docs/img/icons/Blocknet_symbol_dark_RGB.png",
|
||||
"symbol": "ABLOCK",
|
||||
"name": "ANY Blocknet",
|
||||
"contractAddress": "0xc931f61b1534eb21d8c11b24f3f5ab2471d4ab50"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x1ECd47FF4d9598f89721A2866BFEb99505a413Ed/logo.png",
|
||||
"symbol": "AVME",
|
||||
"name": "AVME",
|
||||
"contractAddress": "0x1ecd47ff4d9598f89721a2866bfeb99505a413ed"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/AAVE/logo.png",
|
||||
"symbol": "AAVE",
|
||||
"name": "Aave",
|
||||
"contractAddress": "0x63a72806098bd3d9520cc43356dd78afe5d386d9"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x9E037dE681CaFA6E661e6108eD9c2bd1AA567Ecd/logo.png",
|
||||
"symbol": "ALBT",
|
||||
"name": "AllianceBlock",
|
||||
"contractAddress": "0x9e037de681cafa6e661e6108ed9c2bd1aa567ecd"
|
||||
},
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x027dbcA046ca156De9622cD1e2D907d375e53aa7/logo.png",
|
||||
"symbol": "AMPL",
|
||||
"name": "Ampleforth",
|
||||
"contractAddress": "0x027dbca046ca156de9622cd1e2d907d375e53aa7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x6C6f910A79639dcC94b4feEF59Ff507c2E843929/logo.png",
|
||||
"symbol": "AAVAXB",
|
||||
"name": "Ankr Avalanche Reward Earning Bond",
|
||||
"contractAddress": "0x6c6f910a79639dcc94b4feef59ff507c2e843929"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x938FE3788222A74924E062120E7BFac829c719Fb/logo.png",
|
||||
"symbol": "APEIN",
|
||||
"name": "Ape In",
|
||||
"contractAddress": "0x938fe3788222a74924e062120e7bfac829c719fb"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/avalaunch-app/xava-protocol/master/logo.png",
|
||||
"symbol": "XAVA",
|
||||
"name": "Avalaunch",
|
||||
"contractAddress": "0xd1c3f94de7e5b45fa4edbba472491a9f4b166fc4"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x9F87C6c30F4b23C7B51Aa7465A9e0A836514700D/logo.png",
|
||||
"symbol": "TERRA",
|
||||
"name": "Avaterra",
|
||||
"contractAddress": "0x9f87c6c30f4b23c7b51aa7465a9e0a836514700d"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x78ea17559B3D2CF85a7F9C2C704eda119Db5E6dE/logo.png",
|
||||
"symbol": "AVE",
|
||||
"name": "Avaware",
|
||||
"contractAddress": "0x78ea17559b3d2cf85a7f9c2c704eda119db5e6de"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x783C08b5F26E3daf8C4681F3bf49844e425b6393/logo.png",
|
||||
"symbol": "AUSD",
|
||||
"name": "Avaware USD",
|
||||
"contractAddress": "0x783c08b5f26e3daf8c4681f3bf49844e425b6393"
|
||||
},
|
||||
{
|
||||
"decimalCount": 6,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x397bBd6A0E41bdF4C3F971731E180Db8Ad06eBc1/logo.png",
|
||||
"symbol": "AVXT",
|
||||
"name": "Avaxtars",
|
||||
"contractAddress": "0x397bbd6a0e41bdf4c3f971731e180db8ad06ebc1"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5/logo.png",
|
||||
"symbol": "QI",
|
||||
"name": "BENQI",
|
||||
"contractAddress": "0x8729438eb15e2c8b576fcc6aecda6a148776c0f5"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/BAT/logo.png",
|
||||
"symbol": "BAT",
|
||||
"name": "Basic Attention Token",
|
||||
"contractAddress": "0x98443b96ea4b0858fdf3219cd13e98c7a4690588"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0xd6070ae98b8069de6B494332d1A1a81B6179D960/logo.png",
|
||||
"symbol": "BIFI",
|
||||
"name": "Beefy.Finance",
|
||||
"contractAddress": "0xd6070ae98b8069de6b494332d1a1a81b6179d960"
|
||||
},
|
||||
{
|
||||
"decimalCount": 6,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x564A341Df6C126f90cf3ECB92120FD7190ACb401/logo.png",
|
||||
"symbol": "TRYB",
|
||||
"name": "BiLira",
|
||||
"contractAddress": "0x564a341df6c126f90cf3ecb92120fd7190acb401"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/BUSD/logo.png",
|
||||
"symbol": "BUSD",
|
||||
"name": "Binance USD",
|
||||
"contractAddress": "0x19860ccb0a68fd4213ab9d8266f7bbf05a8dde98"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xbca7f1998Dc9FFB70b086543a808960a460aBcA7/logo.png",
|
||||
"symbol": "KITTY",
|
||||
"name": "Binary Cat",
|
||||
"contractAddress": "0xbca7f1998dc9ffb70b086543a808960a460abca7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x0f34919404a290e71fc6A510cB4a6aCb8D764b24/logo.png",
|
||||
"symbol": "BLZZ",
|
||||
"name": "Blizz Finance",
|
||||
"contractAddress": "0x0f34919404a290e71fc6a510cb4a6acb8d764b24"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/BooFinance/assets/main/logo.png",
|
||||
"symbol": "BOOFI",
|
||||
"name": "Boo Finance",
|
||||
"contractAddress": "0xb00f1ad977a949a3ccc389ca1d1282a2946963b0"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xF2CfC11093edB5A2DC7F49e70A3a3a9cd4F4FeE4/logo.png",
|
||||
"symbol": "BRIG",
|
||||
"name": "Brig Finance",
|
||||
"contractAddress": "0xf2cfc11093edb5a2dc7f49e70a3a3a9cd4f4fee4"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x8D88e48465F30Acfb8daC0b3E35c9D6D7d36abaf/logo.png",
|
||||
"symbol": "CNR",
|
||||
"name": "Canary",
|
||||
"contractAddress": "0x8d88e48465f30acfb8dac0b3e35c9d6d7d36abaf"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/LINK/logo.png",
|
||||
"symbol": "LINK",
|
||||
"name": "Chainlink",
|
||||
"contractAddress": "0x5947bb275c521040051d82396192181b413227a3"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x7761E2338B35bCEB6BdA6ce477EF012bde7aE611/logo.png",
|
||||
"symbol": "EGG",
|
||||
"name": "Chikn Egg",
|
||||
"contractAddress": "0x7761e2338b35bceb6bda6ce477ef012bde7ae611"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xec3492a2508DDf4FDc0cD76F31f340b30d1793e6/logo.png",
|
||||
"symbol": "CLY",
|
||||
"name": "Colony",
|
||||
"contractAddress": "0xec3492a2508ddf4fdc0cd76f31f340b30d1793e6"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/COMP/logo.png",
|
||||
"symbol": "COMP",
|
||||
"name": "Compound",
|
||||
"contractAddress": "0xc3048e19e76cb9a3aa9d77d8c03c29fc906e2437"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xA32608e873F9DdEF944B24798db69d80Bbb4d1ed/logo.png",
|
||||
"symbol": "CRA",
|
||||
"name": "Crabada",
|
||||
"contractAddress": "0xa32608e873f9ddef944b24798db69d80bbb4d1ed"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/CycleProtocol/assets/master/cycle-logo-round-small.png",
|
||||
"symbol": "CYCLE",
|
||||
"name": "Cycle Token",
|
||||
"contractAddress": "0x81440c939f2c1e34fc7048e518a637205a632a74"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/DAI/logo.png",
|
||||
"symbol": "DAI",
|
||||
"name": "Dai",
|
||||
"contractAddress": "0xd586e7f844cea2f87f50152665bcbc2c279d8d70"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x961C8c0B1aaD0c0b10a51FeF6a867E3091BCef17/logo.png",
|
||||
"symbol": "DYP",
|
||||
"name": "DeFi Yield Protocol",
|
||||
"contractAddress": "0x961c8c0b1aad0c0b10a51fef6a867e3091bcef17"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x47EB6F7525C1aA999FBC9ee92715F5231eB1241D/logo.png",
|
||||
"symbol": "MELT",
|
||||
"name": "Defrost Finance",
|
||||
"contractAddress": "0x47eb6f7525c1aa999fbc9ee92715f5231eb1241d"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x1DB749847C4abB991d8B6032102383e6BfD9B1c7/logo.png",
|
||||
"symbol": "DON",
|
||||
"name": "Dogeon",
|
||||
"contractAddress": "0x1db749847c4abb991d8b6032102383e6bfd9b1c7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x100Cc3a819Dd3e8573fD2E46D1E66ee866068f30/logo.png",
|
||||
"symbol": "DCAU",
|
||||
"name": "Dragon Crypto Aurum",
|
||||
"contractAddress": "0x100cc3a819dd3e8573fd2e46d1e66ee866068f30"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xAcD7B3D9c10e97d0efA418903C0c7669E702E4C0/logo.png",
|
||||
"symbol": "ELE",
|
||||
"name": "Eleven Finance",
|
||||
"contractAddress": "0xacd7b3d9c10e97d0efa418903c0c7669e702e4c0"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xE1C110E1B1b4A1deD0cAf3E42BfBdbB7b5d7cE1C/logo.png",
|
||||
"symbol": "ELK",
|
||||
"name": "Elk Finance",
|
||||
"contractAddress": "0xe1c110e1b1b4a1ded0caf3e42bfbdbb7b5d7ce1c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xf6d46849DB378AE01D93732585BEc2C4480D1fD5/logo.png",
|
||||
"symbol": "FORT",
|
||||
"name": "Fortress",
|
||||
"contractAddress": "0xf6d46849db378ae01d93732585bec2c4480d1fd5"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xD24C2Ad096400B6FBcd2ad8B24E7acBc21A1da64/logo.png",
|
||||
"symbol": "FRAX",
|
||||
"name": "Frax",
|
||||
"contractAddress": "0xd24c2ad096400b6fbcd2ad8b24e7acbc21a1da64"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x214DB107654fF987AD859F34125307783fC8e387/logo.png",
|
||||
"symbol": "FXS",
|
||||
"name": "Frax Share",
|
||||
"contractAddress": "0x214db107654ff987ad859f34125307783fc8e387"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x595c8481c48894771CE8FaDE54ac6Bf59093F9E8/logo.png",
|
||||
"symbol": "GAJ",
|
||||
"name": "Gaj Finance",
|
||||
"contractAddress": "0x595c8481c48894771ce8fade54ac6bf59093f9e8"
|
||||
},
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x90842eb834cFD2A1DB0b1512B254a18E4D396215/logo.png",
|
||||
"symbol": "GB",
|
||||
"name": "Good Bridging",
|
||||
"contractAddress": "0x90842eb834cfd2a1db0b1512b254a18e4d396215"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x321E7092a180BB43555132ec53AaA65a5bF84251/logo.png",
|
||||
"symbol": "GOHM",
|
||||
"name": "Governance OHM",
|
||||
"contractAddress": "0x321e7092a180bb43555132ec53aaa65a5bf84251"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xB15f02F9Da8CD1f99E9dd375F21dc96D25ddd82C/logo.png",
|
||||
"symbol": "HERMES",
|
||||
"name": "HERMES",
|
||||
"contractAddress": "0xb15f02f9da8cd1f99e9dd375f21dc96d25ddd82c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xfa4B6db72A650601E7Bd50a0A9f537c9E98311B2/logo.png",
|
||||
"symbol": "HSHARES",
|
||||
"name": "Hermes Shares",
|
||||
"contractAddress": "0xfa4b6db72a650601e7bd50a0a9f537c9e98311b2"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xC7f4debC8072e23fe9259A5C0398326d8EfB7f5c/logo.png",
|
||||
"symbol": "HEC",
|
||||
"name": "HeroesChained",
|
||||
"contractAddress": "0xc7f4debc8072e23fe9259a5c0398326d8efb7f5c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xce0052745A182fff07aCB9dd834041E5F29319b2/logo.png",
|
||||
"symbol": "HIGGS",
|
||||
"name": "Higgs",
|
||||
"contractAddress": "0xce0052745a182fff07acb9dd834041e5f29319b2"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x45C13620B55C35A5f539d26E88247011Eb10fDbd/logo.png",
|
||||
"symbol": "HCT",
|
||||
"name": "HurricaneSwap Token",
|
||||
"contractAddress": "0x45c13620b55c35a5f539d26e88247011eb10fdbd"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xF891214fdcF9cDaa5fdC42369eE4F27F226AdaD6/logo.png",
|
||||
"symbol": "IME",
|
||||
"name": "Imperium Empires",
|
||||
"contractAddress": "0xf891214fdcf9cdaa5fdc42369ee4f27f226adad6"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xeA6887e4a9CdA1B77E70129E5Fba830CdB5cdDef/logo.png",
|
||||
"symbol": "IMX",
|
||||
"name": "Impermax",
|
||||
"contractAddress": "0xea6887e4a9cda1b77e70129e5fba830cdb5cddef"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x544c42fBB96B39B21DF61cf322b5EDC285EE7429/logo.png",
|
||||
"symbol": "INSUR",
|
||||
"name": "InsurAce",
|
||||
"contractAddress": "0x544c42fbb96b39b21df61cf322b5edc285ee7429"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd/logo.png",
|
||||
"symbol": "JOE",
|
||||
"name": "JOE",
|
||||
"contractAddress": "0x6e84a6216ea6dacc71ee8e6b0a5b7322eebc0fdd"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xb27c8941a7Df8958A1778c0259f76D1F8B711C35/logo.png",
|
||||
"symbol": "KLO",
|
||||
"name": "Kalao",
|
||||
"contractAddress": "0xb27c8941a7df8958a1778c0259f76d1f8b711c35"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xf32398dae246C5f672B52A54e9B413dFFcAe1A44/logo.png",
|
||||
"symbol": "KACY",
|
||||
"name": "Kassandra",
|
||||
"contractAddress": "0xf32398dae246c5f672b52a54e9b413dffcae1a44"
|
||||
},
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x5684a087C739A2e845F4AaAaBf4FBd261edc2bE8/logo.png",
|
||||
"symbol": "LF",
|
||||
"name": "Life DAO",
|
||||
"contractAddress": "0x5684a087c739a2e845f4aaaabf4fbd261edc2be8"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x4C9B4E1AC6F24CdE3660D5E4Ef1eBF77C710C084/logo.png",
|
||||
"symbol": "LYD",
|
||||
"name": "Lydia Finance",
|
||||
"contractAddress": "0x4c9b4e1ac6f24cde3660d5e4ef1ebf77c710c084"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x130966628846BFd36ff31a822705796e8cb8C18D/logo.png",
|
||||
"symbol": "MIM",
|
||||
"name": "Magic Internet Money",
|
||||
"contractAddress": "0x130966628846bfd36ff31a822705796e8cb8c18d"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/MKR/logo.png",
|
||||
"symbol": "MKR",
|
||||
"name": "Maker",
|
||||
"contractAddress": "0x88128fd4b259552a9a1d457f435a6527aab72d42"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x9Fda7cEeC4c18008096C2fE2B85F05dc300F94d0/logo.png",
|
||||
"symbol": "MFI",
|
||||
"name": "Marginswap",
|
||||
"contractAddress": "0x9fda7ceec4c18008096c2fe2b85f05dc300f94d0"
|
||||
},
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x7C08413cbf02202a1c13643dB173f2694e0F73f0/logo.png",
|
||||
"symbol": "MAXI",
|
||||
"name": "Maximizer",
|
||||
"contractAddress": "0x7c08413cbf02202a1c13643db173f2694e0f73f0"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x885d748C00A279B67A7749EC6b03301700dd0455/logo.png",
|
||||
"symbol": "MAXI",
|
||||
"name": "Maximus",
|
||||
"contractAddress": "0x885d748c00a279b67a7749ec6b03301700dd0455"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x921f99719Eb6C01b4B8f0BA7973A7C24891e740A/logo.png",
|
||||
"symbol": "MAGE",
|
||||
"name": "MetaBrands",
|
||||
"contractAddress": "0x921f99719eb6c01b4b8f0ba7973a7c24891e740a"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xd9D90f882CDdD6063959A9d837B05Cb748718A05/logo.png",
|
||||
"symbol": "MORE",
|
||||
"name": "More Token",
|
||||
"contractAddress": "0xd9d90f882cddd6063959a9d837b05cb748718a05"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x9E3Ca00f2d4A9E5d4f0add0900de5f15050812cF/logo.png",
|
||||
"symbol": "NFTD",
|
||||
"name": "NFTrade",
|
||||
"contractAddress": "0x9e3ca00f2d4a9e5d4f0add0900de5f15050812cf"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x937E077aBaEA52d3abf879c9b9d3f2eBd15BAA21/logo.png",
|
||||
"symbol": "OH",
|
||||
"name": "Oh! Finance",
|
||||
"contractAddress": "0x937e077abaea52d3abf879c9b9d3f2ebd15baa21"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x0ebd9537A25f56713E34c45b38F421A1e7191469/logo.png",
|
||||
"symbol": "OOE",
|
||||
"name": "OpenOcean",
|
||||
"contractAddress": "0x0ebd9537a25f56713e34c45b38f421a1e7191469"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x346A59146b9b4a77100D369a3d18E8007A9F46a6/logo.png",
|
||||
"symbol": "AVAI",
|
||||
"name": "Orca AVAI",
|
||||
"contractAddress": "0x346a59146b9b4a77100d369a3d18e8007a9f46a6"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x8B1d98A91F853218ddbb066F20b8c63E782e2430/logo.png",
|
||||
"symbol": "ORCA",
|
||||
"name": "Orca DAO",
|
||||
"contractAddress": "0x8b1d98a91f853218ddbb066f20b8c63e782e2430"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/avalanchec/assets/0x60781C2586D68229fde47564546784ab3fACA982/logo.png",
|
||||
"symbol": "PNG",
|
||||
"name": "Pangolin",
|
||||
"contractAddress": "0x60781c2586d68229fde47564546784ab3faca982"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x69A61f38Df59CBB51962E69C54D39184E21C27Ec/logo.png",
|
||||
"symbol": "PARTY",
|
||||
"name": "PartySwap",
|
||||
"contractAddress": "0x69a61f38df59cbb51962e69c54d39184e21c27ec"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xfB98B335551a418cD0737375a2ea0ded62Ea213b/logo.png",
|
||||
"symbol": "PENDLE",
|
||||
"name": "Pendle",
|
||||
"contractAddress": "0xfb98b335551a418cd0737375a2ea0ded62ea213b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/Penguin-Finance/png-files/main/pefiv2_250x250.png",
|
||||
"symbol": "PEFI",
|
||||
"name": "Penguin Finance",
|
||||
"contractAddress": "0xe896cdeaac9615145c0ca09c8cd5c25bced6384c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x22d4002028f537599bE9f666d1c4Fa138522f9c8/logo.png",
|
||||
"symbol": "PTP",
|
||||
"name": "Platypus Finance",
|
||||
"contractAddress": "0x22d4002028f537599be9f666d1c4fa138522f9c8"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x7b2B702706D9b361dfE3f00bD138C0CFDA7FB2Cf/logo.png",
|
||||
"symbol": "PLN",
|
||||
"name": "Pollen",
|
||||
"contractAddress": "0x7b2b702706d9b361dfe3f00bd138c0cfda7fb2cf"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x97Cd1CFE2ed5712660bb6c14053C0EcB031Bff7d/logo.png",
|
||||
"symbol": "RAI",
|
||||
"name": "Rai Reflex Index",
|
||||
"contractAddress": "0x97cd1cfe2ed5712660bb6c14053c0ecb031bff7d"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xb2a85C5ECea99187A977aC34303b80AcbDdFa208/logo.png",
|
||||
"symbol": "ROCO",
|
||||
"name": "Roco Finance",
|
||||
"contractAddress": "0xb2a85c5ecea99187a977ac34303b80acbddfa208"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0xa5E59761eBD4436fa4d20E1A27cBa29FB2471Fc6/logo.png",
|
||||
"symbol": "SHERPA",
|
||||
"name": "Sherpa",
|
||||
"contractAddress": "0xa5e59761ebd4436fa4d20e1a27cba29fb2471fc6"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/Snowball-Finance/Assets/main/Uphill%20snowball.png",
|
||||
"symbol": "SNOB",
|
||||
"name": "Snowball",
|
||||
"contractAddress": "0xc38f41a296a4493ff429f1238e030924a1542e50"
|
||||
},
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x7d1232B90D3F809A54eeaeeBC639C62dF8a8942f/logo.png",
|
||||
"symbol": "SB",
|
||||
"name": "Snowbank",
|
||||
"contractAddress": "0x7d1232b90d3f809a54eeaeebc639c62df8a8942f"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x721C299E6BF7D6a430d9bEA3364Ea197314bcE09/logo.png",
|
||||
"symbol": "MILK2",
|
||||
"name": "Spaceswap MILK2",
|
||||
"contractAddress": "0x721c299e6bf7d6a430d9bea3364ea197314bce09"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xC1d02E488a9Ce2481BFdcd797d5373Dd2E70a9C2/logo.png",
|
||||
"symbol": "SHAKE",
|
||||
"name": "Spaceswap SHAKE",
|
||||
"contractAddress": "0xc1d02e488a9ce2481bfdcd797d5373dd2e70a9c2"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xCE1bFFBD5374Dac86a2893119683F4911a2F7814/logo.png",
|
||||
"symbol": "SPELL",
|
||||
"name": "Spell Token",
|
||||
"contractAddress": "0xce1bffbd5374dac86a2893119683f4911a2f7814"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xbD83010eB60F12112908774998F65761cf9f6f9a/logo.png",
|
||||
"symbol": "BOO",
|
||||
"name": "Spookyswap",
|
||||
"contractAddress": "0xbd83010eb60f12112908774998f65761cf9f6f9a"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x37d87e316CB4e35163881fDb6c6Bc0CdBa91dc0A/logo.png",
|
||||
"symbol": "SET",
|
||||
"name": "Stabilize Token",
|
||||
"contractAddress": "0x37d87e316cb4e35163881fdb6c6bc0cdba91dc0a"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xAafd2577Fb67366d3C89DB0d627C49D769ee2e5D/logo.png",
|
||||
"symbol": "SUSD",
|
||||
"name": "Stabilize USD",
|
||||
"contractAddress": "0xaafd2577fb67366d3c89db0d627c49d769ee2e5d"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xF44Fb887334Fa17d2c5c0F970B5D320ab53eD557/logo.png",
|
||||
"symbol": "START",
|
||||
"name": "Starter.xyz",
|
||||
"contractAddress": "0xf44fb887334fa17d2c5c0f970b5d320ab53ed557"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x6AFD5A1ea4b793CC1526d6Dc7e99A608b356eF7b/logo.png",
|
||||
"symbol": "STORM",
|
||||
"name": "Storm Token",
|
||||
"contractAddress": "0x6afd5a1ea4b793cc1526d6dc7e99a608b356ef7b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/SUSHI/logo.png",
|
||||
"symbol": "SUSHI",
|
||||
"name": "Sushi",
|
||||
"contractAddress": "0x37b608519f91f70f2eeb0e5ed9af4061722e4f76"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x1f1E7c893855525b303f99bDF5c3c05Be09ca251/logo.png",
|
||||
"symbol": "SYN",
|
||||
"name": "Synapse",
|
||||
"contractAddress": "0x1f1e7c893855525b303f99bdf5c3c05be09ca251"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/SNX/logo.png",
|
||||
"symbol": "SNX",
|
||||
"name": "Synthetix Network Token",
|
||||
"contractAddress": "0xbec243c995409e6520d7c41e404da5deba4b209b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x8aE8be25C23833e0A01Aa200403e826F611f9CD2/logo.png",
|
||||
"symbol": "CRAFT",
|
||||
"name": "TaleCraft",
|
||||
"contractAddress": "0x8ae8be25c23833e0a01aa200403e826f611f9cd2"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x094bd7B2D99711A1486FB94d4395801C6d0fdDcC/logo.png",
|
||||
"symbol": "TEDDY",
|
||||
"name": "Teddy",
|
||||
"contractAddress": "0x094bd7b2d99711a1486fb94d4395801c6d0fddcc"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x4fbf0429599460D327BD5F55625E30E4fC066095/logo.png",
|
||||
"symbol": "TSD",
|
||||
"name": "Teddy Dollar",
|
||||
"contractAddress": "0x4fbf0429599460d327bd5f55625e30e4fc066095"
|
||||
},
|
||||
{
|
||||
"decimalCount": 6,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/USDT/logo.png",
|
||||
"symbol": "USDT",
|
||||
"name": "Tether",
|
||||
"contractAddress": "0xc7198437980c041c805a1edcba50c1ce5db95118"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/GRT/logo.png",
|
||||
"symbol": "GRT",
|
||||
"name": "The Graph",
|
||||
"contractAddress": "0x8a0cac13c7da965a312f08ea4229c37869e85cb9"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xfcc6CE74f4cd7eDEF0C5429bB99d38A3608043a5/logo.png",
|
||||
"symbol": "FIRE",
|
||||
"name": "The Phoenix",
|
||||
"contractAddress": "0xfcc6ce74f4cd7edef0c5429bb99d38a3608043a5"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x8F47416CaE600bccF9530E9F3aeaA06bdD1Caa79/logo.png",
|
||||
"symbol": "THOR",
|
||||
"name": "Thor",
|
||||
"contractAddress": "0x8f47416cae600bccf9530e9f3aeaa06bdd1caa79"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xc12e249FaBe1c5Eb7C558E5F50D187687a244E31/logo.png",
|
||||
"symbol": "BLUE",
|
||||
"name": "Tip.Blue",
|
||||
"contractAddress": "0xc12e249fabe1c5eb7c558e5f50d187687a244e31"
|
||||
},
|
||||
{
|
||||
"decimalCount": 8,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xae9d2385Ff2E2951Dd4fA061e74c4d3deDD24347/logo.png",
|
||||
"symbol": "TOK",
|
||||
"name": "Tokenplace",
|
||||
"contractAddress": "0xae9d2385ff2e2951dd4fa061e74c4d3dedd24347"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xb84527D59b6Ecb96F433029ECc890D4492C5dCe1/logo.png",
|
||||
"symbol": "TOMB",
|
||||
"name": "Tomb",
|
||||
"contractAddress": "0xb84527d59b6ecb96f433029ecc890d4492c5dce1"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xf693248F96Fe03422FEa95aC0aFbBBc4a8FdD172/logo.png",
|
||||
"symbol": "TUS",
|
||||
"name": "Treasure Under Sea",
|
||||
"contractAddress": "0xf693248f96fe03422fea95ac0afbbbc4a8fdd172"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x1C20E891Bab6b1727d14Da358FAe2984Ed9B59EB/logo.png",
|
||||
"symbol": "TUSD",
|
||||
"name": "TrueUSD",
|
||||
"contractAddress": "0x1c20e891bab6b1727d14da358fae2984ed9b59eb"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xc7B5D72C836e718cDA8888eaf03707fAef675079/logo.png",
|
||||
"symbol": "SWAP",
|
||||
"name": "Trustswap",
|
||||
"contractAddress": "0xc7b5d72c836e718cda8888eaf03707faef675079"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x21c5402C3B7d40C89Cc472C9dF5dD7E51BbAb1b1/logo.png",
|
||||
"symbol": "TUNDRA",
|
||||
"name": "Tundra Token",
|
||||
"contractAddress": "0x21c5402c3b7d40c89cc472c9df5dd7e51bbab1b1"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/UMA/logo.png",
|
||||
"symbol": "UMA",
|
||||
"name": "UMA",
|
||||
"contractAddress": "0x3bd2b1c7ed8d396dbb98ded3aebb41350a5b2339"
|
||||
},
|
||||
{
|
||||
"decimalCount": 6,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E/logo.png",
|
||||
"symbol": "USDC",
|
||||
"name": "USD Coin",
|
||||
"contractAddress": "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e"
|
||||
},
|
||||
{
|
||||
"decimalCount": 6,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/USDC/logo.png",
|
||||
"symbol": "USDC",
|
||||
"name": "USD Coin Avalanche Bridged (USDC.e)",
|
||||
"contractAddress": "0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/UNI/logo.png",
|
||||
"symbol": "UNI",
|
||||
"name": "Uniswap",
|
||||
"contractAddress": "0x8ebaf22b6f053dffeaf46f4dd9efa95d89ba8580"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/VersoOfficial/pr/master/icon_blue.png",
|
||||
"symbol": "VSO",
|
||||
"name": "Verso",
|
||||
"contractAddress": "0x846d50248baf8b7ceaa9d9b53bfd12d7d7fbb25a"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/WETH/logo.png",
|
||||
"symbol": "WETH",
|
||||
"name": "WETH",
|
||||
"contractAddress": "0x49d5c2bdffac6ce2bfdb6640f4f80f226bc10bab"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/WOO/logo.png",
|
||||
"symbol": "WOO",
|
||||
"name": "WOO Network",
|
||||
"contractAddress": "0xabc9547b534519ff73921b1fba6e672b5f58d083"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xA384Bc7Cdc0A93e686da9E7B8C0807cD040F4E0b/logo.png",
|
||||
"symbol": "WOW",
|
||||
"name": "WOWswap",
|
||||
"contractAddress": "0xa384bc7cdc0a93e686da9e7b8c0807cd040f4e0b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xB1466d4cf0DCfC0bCdDcf3500F473cdACb88b56D/logo.png",
|
||||
"symbol": "WET",
|
||||
"name": "Weble Ecosystem Token",
|
||||
"contractAddress": "0xb1466d4cf0dcfc0bcddcf3500f473cdacb88b56d"
|
||||
},
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xb54f16fB19478766A268F172C9480f8da1a7c9C3/logo.png",
|
||||
"symbol": "TIME",
|
||||
"name": "Wonderland",
|
||||
"contractAddress": "0xb54f16fb19478766a268f172c9480f8da1a7c9c3"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7/logo.png",
|
||||
"symbol": "WAVAX",
|
||||
"name": "Wrapped AVAX",
|
||||
"contractAddress": "0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/bridge-tokens/main/avalanche-tokens/0x264c1383EA520f73dd837F915ef3a732e204a493/logo.png",
|
||||
"symbol": "WBNB",
|
||||
"name": "Wrapped BNB",
|
||||
"contractAddress": "0x264c1383ea520f73dd837f915ef3a732e204a493"
|
||||
},
|
||||
{
|
||||
"decimalCount": 8,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/WBTC/logo.png",
|
||||
"symbol": "WBTC",
|
||||
"name": "Wrapped Bitcoin",
|
||||
"contractAddress": "0x50b7545627a5162f82a992c33b87adc75187b218"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x01C2086faCFD7aA38f69A6Bd8C91BEF3BB5adFCa/logo.png",
|
||||
"symbol": "YAY",
|
||||
"name": "YAY Games",
|
||||
"contractAddress": "0x01c2086facfd7aa38f69a6bd8c91bef3bb5adfca"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/YetiSwap/yetiswap.app/master/src/assets/image/YTSCoin.png",
|
||||
"symbol": "YETI",
|
||||
"name": "Yearn Ecosystem Token Index",
|
||||
"contractAddress": "0x488f73cddda1de3664775ffd91623637383d6404"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0x59414b3089ce2AF0010e7523Dea7E2b35d776ec7/logo.png",
|
||||
"symbol": "YAK",
|
||||
"name": "Yield Yak",
|
||||
"contractAddress": "0x59414b3089ce2af0010e7523dea7e2b35d776ec7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xab592d197ACc575D16C3346f4EB70C703F308D1E/logo.png",
|
||||
"symbol": "FEED",
|
||||
"name": "chikn feed",
|
||||
"contractAddress": "0xab592d197acc575d16c3346f4eb70c703f308d1e"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/pangolindex/tokens/main/assets/0xBD100d061E120b2c67A24453CF6368E63f1Be056/logo.png",
|
||||
"symbol": "IDYP",
|
||||
"name": "iDeFiYieldProtocol",
|
||||
"contractAddress": "0xbd100d061e120b2c67a24453cf6368e63f1be056"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/ava-labs/avalanche-bridge-resources/main/tokens/YFI/logo.png",
|
||||
"symbol": "YFI",
|
||||
"name": "yearn.finance",
|
||||
"contractAddress": "0x9eaac1b23d935365bd7b542fe22ceee2922f52dc"
|
||||
}
|
||||
]
|
||||
16
app/src/main/assets/avalanche_tokens_testnet.json
Normal file
16
app/src/main/assets/avalanche_tokens_testnet.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
[
|
||||
{
|
||||
"decimalCount": 6,
|
||||
"customIconUrl": "https://i.imgur.com\/LPZDXoT.png",
|
||||
"symbol": "FUJISTABLE",
|
||||
"name": "The Fuji stablecoin",
|
||||
"contractAddress": "0x2058ec2791dD28b6f67DB836ddf87534F4Bbdf22"
|
||||
},
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"customIconUrl": "https://i.imgur.com\/jNTqyQA.png",
|
||||
"symbol": "FUJIMOON",
|
||||
"name": "To the Moon",
|
||||
"contractAddress": "0x97132C109c6816525F7f338DCb7435E1412A7668"
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,14 +1,9 @@
|
|||
[
|
||||
{
|
||||
"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"
|
||||
"decimalCount": 8,
|
||||
"customIconUrl": "https:\/\/assets.trustwalletapp.com\/blockchains\/binance\/assets\/MATIC-84A\/logo.png",
|
||||
"symbol": "HEM",
|
||||
"name": "Hemster - 452",
|
||||
"contractAddress": "HEM-452"
|
||||
}
|
||||
]
|
||||
]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,99 +1,65 @@
|
|||
[
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c/logo.png",
|
||||
"symbol": "BTCB",
|
||||
"name": "Binance-Peg BTCB Token",
|
||||
"contractAddress": "0x6ce8da28e2f864420840cf74474eff5fd80e65b8"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56/logo.png",
|
||||
"symbol": "BUSD",
|
||||
"name": "Binance-Peg BUSD Token",
|
||||
"contractAddress": "0xed24fc36d5ee211ea25a80239fb8c4cfd80f12ee"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3/logo.png",
|
||||
"symbol": "DAI",
|
||||
"name": "Binance-Peg Dai Token",
|
||||
"contractAddress": "0xec5dcb5dbf4b114c9d0f65bccab49ec54f6a0867"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x55d398326f99059fF775485246999027B3197955/logo.png",
|
||||
"symbol": "USDT",
|
||||
"name": "Binance-Peg Tether USD",
|
||||
"contractAddress": "0x337610d27c682e347c9cd60bd4b3b107c9d34ddd"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"symbol": "USDC",
|
||||
"name": "Binance-Peg USDC Token",
|
||||
"contractAddress": "0x64544969ed7ebf5f083679233325356ebe738930"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1D2F0da169ceB9fC7B3144628dB156f3F6c60dBE/logo.png",
|
||||
"symbol": "XRP",
|
||||
"name": "Binance-Peg XRP",
|
||||
"contractAddress": "0xa83575490d7df4e2f47b7d38ef351a2722ca45b9"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3/logo.png",
|
||||
"symbol": "DAI",
|
||||
"name": "Dai Token",
|
||||
"contractAddress": "0x8a9424745056eb399fd19a0ec26a14316684e274"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x2170Ed0880ac9A755fd29B2688956BD959F933F8/logo.png",
|
||||
"symbol": "ETH",
|
||||
"name": "Ethereum Token",
|
||||
"contractAddress": "0xd66c6b4f0be8ce5b39d52e0fd1344c389929b378"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://assets.trustwalletapp.com/blockchains/smartchain/assets/0x55d398326f99059fF775485246999027B3197955/logo.png",
|
||||
"symbol": "USDT",
|
||||
"name": "Tether USD",
|
||||
"contractAddress": "0x7ef95a0fee0dd31b22626fa2e10ee6a223f8a684"
|
||||
}
|
||||
]
|
||||
9984
app/src/main/assets/eth_tokens.json
Normal file
9984
app/src/main/assets/eth_tokens.json
Normal file
File diff suppressed because it is too large
Load diff
26
app/src/main/assets/eth_tokens_testnet.json
Normal file
26
app/src/main/assets/eth_tokens_testnet.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
[
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"symbol": "WEENUS",
|
||||
"name": "Weenus",
|
||||
"contractAddress": "0xaFF4481D10270F50f203E0763e2597776068CBc5"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"symbol": "XEENUS",
|
||||
"name": "Xeenus",
|
||||
"contractAddress": "0x022E292b44B5a146F2e8ee36Ff44D3dd863C915c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 8,
|
||||
"symbol": "YEENUS",
|
||||
"name": "Yeenus",
|
||||
"contractAddress": "0xc6fDe3FD2Cc2b173aEC24cc3f267cb3Cd78a26B7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 0,
|
||||
"symbol": "ZEENUS",
|
||||
"name": "Zeenus",
|
||||
"contractAddress": "0x1f9061B953bBa0E36BF50F21876132DcF276fC6e"
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,46 +0,0 @@
|
|||
[
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
]
|
||||
868
app/src/main/assets/polygon_tokens.json
Normal file
868
app/src/main/assets/polygon_tokens.json
Normal file
|
|
@ -0,0 +1,868 @@
|
|||
[
|
||||
{
|
||||
"decimalCount": 8,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x71B821aa52a49F32EEd535fCA6Eb5aa130085978/logo.png",
|
||||
"symbol": "0XBTC",
|
||||
"name": "0xBitcoin",
|
||||
"contractAddress": "0x71b821aa52a49f32eed535fca6eb5aa130085978"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x111111111117dc0aa78b770fa6a738034120c302.png",
|
||||
"symbol": "1INCH",
|
||||
"name": "1inch",
|
||||
"contractAddress": "0x9c2c5fd7b07e95ee044ddeba0e97a665f142394f"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x48cBc913dE09317dF2365e6827Df50dA083701D5/logo.png",
|
||||
"symbol": "FOUR",
|
||||
"name": "4thpillar technologies",
|
||||
"contractAddress": "0x48cbc913de09317df2365e6827df50da083701d5"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x034b2090b579228482520c589dbD397c53Fc51cC.png",
|
||||
"symbol": "VISION",
|
||||
"name": "APY.vision",
|
||||
"contractAddress": "0x034b2090b579228482520c589dbd397c53fc51cc"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/aave.svg",
|
||||
"symbol": "AAVE",
|
||||
"name": "Aave",
|
||||
"contractAddress": "0xd6df932a45c0f255f85145f286ea0b292b21c90b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0.png",
|
||||
"symbol": "AMWMATIC",
|
||||
"name": "Aave Polygon WMATIC",
|
||||
"contractAddress": "0x8df3aad3a84da6b69a4da8aec3ea40d9091b2ac4"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://polygonscan.com/token/images/aavegotchighst_32.png",
|
||||
"symbol": "GHST",
|
||||
"name": "Aavegotchi",
|
||||
"contractAddress": "0x385eeac5cb85a38a9a07a70c73e0a3271cfb54a7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xc3FdbadC7c795EF1D6Ba111e06fF8F16A20Ea539/logo.png",
|
||||
"symbol": "ADDY",
|
||||
"name": "Adamant",
|
||||
"contractAddress": "0xc3fdbadc7c795ef1d6ba111e06ff8f16a20ea539"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xcae72a7a0fd9046cf6b165ca54c9e3a3872109e0.png",
|
||||
"symbol": "$ANRX",
|
||||
"name": "AnRKey X",
|
||||
"contractAddress": "0x554f074d9ccda8f483d1812d4874cbebd682644e"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"symbol": "ANY",
|
||||
"name": "Anyswap",
|
||||
"contractAddress": "0x6ab6d61428fde76768d7b45d8bfeec19c6ef91a8"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x5d47bAbA0d66083C52009271faF3F50DCc01023C/logo.png",
|
||||
"symbol": "BANANA",
|
||||
"name": "ApeSwap Finance",
|
||||
"contractAddress": "0x5d47baba0d66083c52009271faf3f50dcc01023c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 0,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xdacd69347de42babfaecd09dc88958378780fb62.png",
|
||||
"symbol": "ATRI",
|
||||
"name": "Atari",
|
||||
"contractAddress": "0xb140665dde25c644c6b418e417c930de8a8a6ac9"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x76e63a3E7Ba1e2E61D3DA86a87479f983dE89a7E/logo.png",
|
||||
"symbol": "OMEN",
|
||||
"name": "Augury Finance",
|
||||
"contractAddress": "0x76e63a3e7ba1e2e61d3da86a87479f983de89a7e"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x7f426F6Dc648e50464a0392E60E1BB465a67E9cf.png",
|
||||
"symbol": "AUTO",
|
||||
"name": "Auto",
|
||||
"contractAddress": "0x7f426f6dc648e50464a0392e60e1bb465a67e9cf"
|
||||
},
|
||||
{
|
||||
"decimalCount": 4,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xc813ea5e3b48bebeedb796ab42a30c5599b01740.png",
|
||||
"symbol": "NIOX",
|
||||
"name": "Autonio",
|
||||
"contractAddress": "0xad684e79ce4b6d464f2ff7c3fd51646892e24b96"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x340d2bde5eb28c1eed91b2f790723e3b160613b7.png",
|
||||
"symbol": "VEE",
|
||||
"name": "BLOCKv",
|
||||
"contractAddress": "0xf1c1a3c2481a3a8a3f173a9ab5ade275292a6fa3"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xb683d83a532e2cb7dfa5275eed3698436371cc9f.png",
|
||||
"symbol": "BTU",
|
||||
"name": "BTU Protocol",
|
||||
"contractAddress": "0xfdc26cda2d2440d0e83cd1dee8e8be48405806dc"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/bal.svg",
|
||||
"symbol": "BAL",
|
||||
"name": "Balancer",
|
||||
"contractAddress": "0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x2d94AA3e47d9D5024503Ca8491fcE9A2fB4DA198.png",
|
||||
"symbol": "BANK",
|
||||
"name": "Bankless DAO",
|
||||
"contractAddress": "0xdb7cb471dd0b49b29cab4a1c14d070f27216a0ab"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x0d8775f648430679a709e98d2b0cb6250d2887ef.png",
|
||||
"symbol": "BAT",
|
||||
"name": "Basic Attention Token",
|
||||
"contractAddress": "0x3cef98bb43d732e2f285ee605a8158cde967d219"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xFbdd194376de19a88118e84E279b977f165d01b8.png",
|
||||
"symbol": "BIFI",
|
||||
"name": "Beefy.Finance",
|
||||
"contractAddress": "0xfbdd194376de19a88118e84e279b977f165d01b8"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/busd.svg",
|
||||
"symbol": "BUSD",
|
||||
"name": "Binance USD",
|
||||
"contractAddress": "0xdab529f40e671a1d4bf91361c21bf9f0c9712ab7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x229b1b6C23ff8953D663C4cBB519717e323a0a84/logo.png",
|
||||
"symbol": "BLOK",
|
||||
"name": "Bloktopia",
|
||||
"contractAddress": "0x229b1b6c23ff8953d663c4cbb519717e323a0a84"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x491604c0fdf08347dd1fa4ee062a822a5dd06b5d.png",
|
||||
"symbol": "CTSI",
|
||||
"name": "Cartesi",
|
||||
"contractAddress": "0x2727ab1c2d22170abc9b595177b2d5c6e1ab7b7b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 4,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xaaaebe6fe48e54f431b0c390cfaf0b017d09d42d.png",
|
||||
"symbol": "CEL",
|
||||
"name": "Celsius Network",
|
||||
"contractAddress": "0xd85d1e945766fea5eda9103f918bd915fbca63e6"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/link.svg",
|
||||
"symbol": "LINK",
|
||||
"name": "Chainlink",
|
||||
"contractAddress": "0x53e0bca35ec356bd5dddfebbd1fc0fd03fabad39"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xE33dC9d2D7D64c1e23467aC7eD7F8eF56d0a8d66/logo.png",
|
||||
"symbol": "CHERRY",
|
||||
"name": "CherryPick",
|
||||
"contractAddress": "0xe33dc9d2d7d64c1e23467ac7ed7f8ef56d0a8d66"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x9b31bb425D8263fA1b8B9d090b83CF0C31665355.png",
|
||||
"symbol": "CPD",
|
||||
"name": "CoinsPaid",
|
||||
"contractAddress": "0x1ce4a2c355f0dcc24e32a9af19f1836d6f4f98ae"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xc00e94cb662c3520282e6f5717214004a7f26888.png",
|
||||
"symbol": "COMP",
|
||||
"name": "Compound",
|
||||
"contractAddress": "0x8505b9d2254a7ae468c0e9dd10ccea3a837aef5c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x172370d5Cd63279eFa6d502DAB29171933a610AF/logo.png",
|
||||
"symbol": "CRV",
|
||||
"name": "Curve DAO Token",
|
||||
"contractAddress": "0x172370d5cd63279efa6d502dab29171933a610af"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x63b4f3e3fa4e438698ce330e365e831f7ccd1ef4.png",
|
||||
"symbol": "CFI",
|
||||
"name": "CyberFi",
|
||||
"contractAddress": "0xecf8f2fa183b1c4d2a269bf98a54fce86c812d3e"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xfa6de2697d59e88ed7fc4dfe5a33dac43565ea41.png",
|
||||
"symbol": "DEFI5",
|
||||
"name": "DEFI Top 5 Index",
|
||||
"contractAddress": "0x42435f467d33e5c4146a4e8893976ef12bbce762"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xDE12c7959E1a72bbe8a5f7A1dc8f8EeF9Ab011B3/logo.png",
|
||||
"symbol": "DEI",
|
||||
"name": "DEI Token",
|
||||
"contractAddress": "0xde12c7959e1a72bbe8a5f7a1dc8f8eef9ab011b3"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xDE5ed76E7c05eC5e4572CfC88d1ACEA165109E44/logo.png",
|
||||
"symbol": "DEUS",
|
||||
"name": "DEUS Finance",
|
||||
"contractAddress": "0xde5ed76e7c05ec5e4572cfc88d1acea165109e44"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x43dfc4159d86f3a37a5a4b3d4580b888ad7d4ddd.png",
|
||||
"symbol": "DODO",
|
||||
"name": "DODO",
|
||||
"contractAddress": "0xe4bf2864ebec7b7fdf6eeca9bacae7cdfdaffe78"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/dai.svg",
|
||||
"symbol": "DAI",
|
||||
"name": "Dai",
|
||||
"contractAddress": "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b.png",
|
||||
"symbol": "DPI",
|
||||
"name": "DeFi Pulse Index",
|
||||
"contractAddress": "0x85955046df4668e1dd369d2de9f3aeb98dd2a369"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xef938b6da8576a896f6E0321ef80996F4890f9c4/logo.png",
|
||||
"symbol": "DG",
|
||||
"name": "Decentral Games",
|
||||
"contractAddress": "0xef938b6da8576a896f6e0321ef80996f4890f9c4"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x0f5d2fb29fb7d3cfee444a200298f468908cc942.png",
|
||||
"symbol": "MANA",
|
||||
"name": "Decentraland",
|
||||
"contractAddress": "0xa1c57f48f0deb89f569dfbe6e2b7f46d33606fd4"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xC168E40227E4ebD8C1caE80F7a55a4F0e6D66C97/logo.png",
|
||||
"symbol": "DFYN",
|
||||
"name": "Dfyn Network",
|
||||
"contractAddress": "0xc168e40227e4ebd8c1cae80f7a55a4f0e6d66c97"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xB9638272aD6998708de56BBC0A290a1dE534a578.png",
|
||||
"symbol": "IQ",
|
||||
"name": "Everipedia",
|
||||
"contractAddress": "0xb9638272ad6998708de56bbc0a290a1de534a578"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xee573a945b01b788b9287ce062a0cfc15be9fd86.png",
|
||||
"symbol": "XED",
|
||||
"name": "Exeedme",
|
||||
"contractAddress": "0x2fe8733dcb25bfbba79292294347415417510067"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xfffffffff15abf397da76f1dcc1a1604f45126db.png",
|
||||
"symbol": "FSW",
|
||||
"name": "Falconswap",
|
||||
"contractAddress": "0xad5dc12e88c6534eea8cfe2265851d9d4a1472ad"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x853d955aCEf822Db058eb8505911ED77F175b99e.png",
|
||||
"symbol": "FRAX",
|
||||
"name": "Frax",
|
||||
"contractAddress": "0x45c32fa6df82ead1e2ef74d17b76547eddfaff89"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"symbol": "FXS",
|
||||
"name": "Frax Share",
|
||||
"contractAddress": "0x1a3acf6d19267e2d3e7f898f42803e90c9219062"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xf8c3527cc04340b208c854e985240c02f7b7793f.png",
|
||||
"symbol": "FRONT",
|
||||
"name": "Frontier",
|
||||
"contractAddress": "0xa3ed22eee92a3872709823a6970069e12a4540eb"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xcf32822ff397Ef82425153a9dcb726E5fF61DCA7/logo.png",
|
||||
"symbol": "GMEE",
|
||||
"name": "GAMEE",
|
||||
"contractAddress": "0xcf32822ff397ef82425153a9dcb726e5ff61dca7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x7075cAB6bCCA06613e2d071bd918D1a0241379E2.png",
|
||||
"symbol": "GFARM2",
|
||||
"name": "Gains Farm",
|
||||
"contractAddress": "0x7075cab6bcca06613e2d071bd918d1a0241379e2"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x8d1566569d5b695d44a9a234540f68D393cDC40D.png",
|
||||
"symbol": "GAME",
|
||||
"name": "GameCredits",
|
||||
"contractAddress": "0x8d1566569d5b695d44a9a234540f68d393cdc40d"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x3f6b3595ecF70735D3f48D69b09C4E4506DB3F47/logo.png",
|
||||
"symbol": "GAMER",
|
||||
"name": "GameStation",
|
||||
"contractAddress": "0x3f6b3595ecf70735d3f48d69b09c4e4506db3f47"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xbea98c05eeae2f3bc8c3565db7551eb738c8ccab.png",
|
||||
"symbol": "GYSR",
|
||||
"name": "Geyser",
|
||||
"contractAddress": "0xc48f61a288a08f1b80c2edd74652e1276b6a168c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 8,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x2b591e99afe9f32eaa6214f7b7629768c40eeb39.png",
|
||||
"symbol": "HEX",
|
||||
"name": "HEX",
|
||||
"contractAddress": "0x23d29d30e35c5e8d321e1dc9a8a61bfd846d4c5c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x0954906da0bf32d5479e25f46056d22f08464cab.png",
|
||||
"symbol": "INDEX",
|
||||
"name": "Index Cooperative",
|
||||
"contractAddress": "0xfbd8a3b908e764dbcd51e27992464b4432a1132b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xD86b5923F3AD7b585eD81B448170ae026c65ae9a/logo.png",
|
||||
"symbol": "IRON",
|
||||
"name": "Iron",
|
||||
"contractAddress": "0xd86b5923f3ad7b585ed81b448170ae026c65ae9a"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x6AE7Dfc73E0dDE2aa99ac063DcF7e8A63265108c.png",
|
||||
"symbol": "JPYC",
|
||||
"name": "JPY Coin",
|
||||
"contractAddress": "0x6ae7dfc73e0dde2aa99ac063dcf7e8a63265108c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x8a9c67fee641579deba04928c4bc45f66e26343a.png",
|
||||
"symbol": "JRT",
|
||||
"name": "Jarvis Reward Token",
|
||||
"contractAddress": "0x596ebe76e2db4470966ea395b0d063ac6197a8c5"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xdd974d5c2e2928dea5f71b9825b8b646686bd200.png",
|
||||
"symbol": "KNC",
|
||||
"name": "Kyber Network Crystal",
|
||||
"contractAddress": "0x1c954e8fe737f99f68fa1ccda3e51ebdb291948c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x5f98805a4e8be255a32880fdec7f6728c6568ba0.png",
|
||||
"symbol": "LUSD",
|
||||
"name": "Liquity USD",
|
||||
"contractAddress": "0x23001f892c0c82b79303edc9b9033cd190bb21c7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xa3Fa99A148fA48D14Ed51d610c367C61876997F1/logo.png",
|
||||
"symbol": "MIMATIC",
|
||||
"name": "MAI",
|
||||
"contractAddress": "0xa3fa99a148fa48d14ed51d610c367c61876997f1"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x3593d125a4f7849a1b059e64f4517a86dd60c95d.png",
|
||||
"symbol": "OM",
|
||||
"name": "MANTRA DAO",
|
||||
"contractAddress": "0xc3ec80343d2bae2f8e680fdadde7c17e71e114ea"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xb4d930279552397bba2ee473229f89ec245bc365.png",
|
||||
"symbol": "MAHA",
|
||||
"name": "MahaDAO",
|
||||
"contractAddress": "0xedd6ca8a4202d4a36611e2fff109648c4863ae19"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x69af81e73a73b40adf4f3d4223cd9b1ece623074.png",
|
||||
"symbol": "MASK",
|
||||
"name": "Mask Network",
|
||||
"contractAddress": "0x2b9e7ccdf0f4e5b24757c1e1a80e311e34cb10c7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xffc97d72e13e01096502cb8eb52dee56f74dad7b.png",
|
||||
"symbol": "MAAAVE",
|
||||
"name": "Matic Aave Interest Bearing AAVE",
|
||||
"contractAddress": "0x823cd4264c1b951c9209ad0deaea9988fe8429bf"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x028171bca77440897b824ca71d1c56cac55b68a3.png",
|
||||
"symbol": "MADAI",
|
||||
"name": "Matic Aave Interest Bearing DAI",
|
||||
"contractAddress": "0xe0b22e0037b130a9f56bbb537684e6fa18192341"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xa06bc25b5805d5f8d82847d191cb4af5a3e873e0.png",
|
||||
"symbol": "MALINK",
|
||||
"name": "Matic Aave Interest Bearing LINK",
|
||||
"contractAddress": "0x98ea609569bd25119707451ef982b90e3eb719cd"
|
||||
},
|
||||
{
|
||||
"decimalCount": 6,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x9719d867A500Ef117cC201206B8ab51e794d3F82/logo.png",
|
||||
"symbol": "MAUSDC",
|
||||
"name": "Matic Aave Interest Bearing USDC",
|
||||
"contractAddress": "0x9719d867a500ef117cc201206b8ab51e794d3f82"
|
||||
},
|
||||
{
|
||||
"decimalCount": 6,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x3ed3b47dd13ec9a98b44e6204a523e766b225811.png",
|
||||
"symbol": "MAUSDT",
|
||||
"name": "Matic Aave Interest Bearing USDT",
|
||||
"contractAddress": "0xdae5f1590db13e3b40423b5b5c5fbf175515910b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x030ba81f1c18d280636f32af80b9aad02cf0854e.png",
|
||||
"symbol": "MAWETH",
|
||||
"name": "Matic Aave Interest Bearing WETH",
|
||||
"contractAddress": "0x20d3922b4a1a8560e1ac99fba4fade0c849e2142"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x5165d24277cd063f5ac44efd447b27025e888f37.png",
|
||||
"symbol": "MAYFI",
|
||||
"name": "Matic Aave Interest Bearing YFI",
|
||||
"contractAddress": "0xe20f7d1f0ec39c4d5db01f53554f2ef54c71f613"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x101cc05f4a51c0319f570d5e146a8c625198e636.png",
|
||||
"symbol": "MATUSD",
|
||||
"name": "Matic Aave interest bearing TUSD",
|
||||
"contractAddress": "0xf4b8888427b00d7caf21654408b7cba2ecf4ebd9"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x72e364F2ABdC788b7E918bc238B21f109Cd634D7.png",
|
||||
"symbol": "MVI",
|
||||
"name": "Metaverse Index",
|
||||
"contractAddress": "0xfe712251173a2cd5f5be2b46bb528328ea3565e1"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x7778360F035C589fCE2f4eA5786CbD8B36e5396B.png",
|
||||
"symbol": "OOE",
|
||||
"name": "OpenOcean",
|
||||
"contractAddress": "0x9d5565da88e596730522cbc5a918d2a89dbc16d9"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x1631244689EC1fEcbDD22fb5916E920dFC9b8D30/logo.png",
|
||||
"symbol": "OVR",
|
||||
"name": "Ovr",
|
||||
"contractAddress": "0x1631244689ec1fecbdd22fb5916e920dfc9b8d30"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x9aF3b7DC29D3C4B1A5731408B6A9656fA7aC3b72/logo.png",
|
||||
"symbol": "PUSD",
|
||||
"name": "PUSD",
|
||||
"contractAddress": "0x9af3b7dc29d3c4b1a5731408b6a9656fa7ac3b72"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x4C16f69302CcB511c5Fac682c7626B9eF0Dc126a.png",
|
||||
"symbol": "POLYBUNNY",
|
||||
"name": "Pancake Bunny Polygon",
|
||||
"contractAddress": "0x4c16f69302ccb511c5fac682c7626b9ef0dc126a"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xc1c93D475dc82Fe72DBC7074d55f5a734F8cEEAE/logo.png",
|
||||
"symbol": "PGX",
|
||||
"name": "Pegaxy Stone",
|
||||
"contractAddress": "0xc1c93d475dc82fe72dbc7074d55f5a734f8ceeae"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x429881672b9ae42b8eba0e26cd9c73711b891ca5.png",
|
||||
"symbol": "PICKLE",
|
||||
"name": "Pickle Finance",
|
||||
"contractAddress": "0x2b88ad57897a8b496595925f43048301c37615da"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xe3818504c1b32bf1557b16c238b2e01fd3149c17.png",
|
||||
"symbol": "PLR",
|
||||
"name": "Pillar",
|
||||
"contractAddress": "0xa6b37fc85d870711c56fbcb8afe2f8db049ae774"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x054d64b73d3d8a21af3d764efd76bcaa774f3bb2.png",
|
||||
"symbol": "PPAY",
|
||||
"name": "Plasma Finance",
|
||||
"contractAddress": "0x08158a6b5d4018340387d1a302f882e98a8bc5b4"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x72f020f8f3e8fd9382705723cd26380f8d0c66bb.png",
|
||||
"symbol": "PLOT",
|
||||
"name": "PlotX",
|
||||
"contractAddress": "0xe82808eaa78339b06a691fd92e1be79671cad8d3"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xd8912c10681d8b21fd3742244f44658dba12264e.png",
|
||||
"symbol": "PLU",
|
||||
"name": "Pluton",
|
||||
"contractAddress": "0x7dc0cb65ec6019330a6841e9c274f2ee57a6ca6c"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x3a3Df212b7AA91Aa0402B9035b098891d276572B/logo.png",
|
||||
"symbol": "FISH",
|
||||
"name": "Polycat Finance",
|
||||
"contractAddress": "0x3a3df212b7aa91aa0402b9035b098891d276572b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/matic.svg",
|
||||
"symbol": "MATIC",
|
||||
"name": "Polygon",
|
||||
"contractAddress": "0x0000000000000000000000000000000000001010"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xC68e83a305b0FaD69E264A1769a0A070F190D2d6/logo.png",
|
||||
"symbol": "ROLL",
|
||||
"name": "Polyroll",
|
||||
"contractAddress": "0xc68e83a305b0fad69e264a1769a0a070f190d2d6"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x05089C9EBFFa4F0AcA269e32056b1b36B37ED71b/logo.png",
|
||||
"symbol": "KRILL",
|
||||
"name": "Polywhale",
|
||||
"contractAddress": "0x05089c9ebffa4f0aca269e32056b1b36b37ed71b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x580A84C73811E1839F75d86d75d88cCa0c241fF4/logo.png",
|
||||
"symbol": "QI",
|
||||
"name": "Qi Dao",
|
||||
"contractAddress": "0x580a84c73811e1839f75d86d75d88cca0c241ff4"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/quick.svg",
|
||||
"symbol": "QUICK",
|
||||
"name": "Quickswap",
|
||||
"contractAddress": "0x831753dd7087cac61ab5644b308642cc1c33dc13"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x33D0568941C0C64ff7e0FB4fbA0B11BD37deEd9f.png",
|
||||
"symbol": "RAMP",
|
||||
"name": "RAMP",
|
||||
"contractAddress": "0xaecebfcf604ad245eaf0d5bd68459c3a7a6399c2"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x03ab458634910aad20ef5f1c8ee96f1d6ac54919.png",
|
||||
"symbol": "RAI",
|
||||
"name": "Rai Reflex Index",
|
||||
"contractAddress": "0x00e5646f60ac6fb446f621d146b6e1886f002905"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xC91c06DB0f7bFFba61e2A5645CC15686F0a8c828.png",
|
||||
"symbol": "RAZOR",
|
||||
"name": "Razor Network",
|
||||
"contractAddress": "0xc91c06db0f7bffba61e2a5645cc15686f0a8c828"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x61299774020dA444Af134c82fa83E3810b309991.png",
|
||||
"symbol": "RNDR",
|
||||
"name": "Render Token",
|
||||
"contractAddress": "0x61299774020da444af134c82fa83e3810b309991"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x16ECCfDbb4eE1A85A33f3A9B21175Cd7Ae753dB4/logo.png",
|
||||
"symbol": "ROUTE",
|
||||
"name": "Router Protocol",
|
||||
"contractAddress": "0x16eccfdbb4ee1a85a33f3a9b21175cd7ae753db4"
|
||||
},
|
||||
{
|
||||
"decimalCount": 2,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xdb25f211ab05b1c97d595516f45794528a807ad8.png",
|
||||
"symbol": "EURS",
|
||||
"name": "STASIS EURO",
|
||||
"contractAddress": "0xe111178a87a3bff0c8d18decba5798827539ae99"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x840195888Db4D6A99ED9F73FcD3B225Bb3cB1A79.png",
|
||||
"symbol": "SX",
|
||||
"name": "SX Network",
|
||||
"contractAddress": "0x840195888db4d6a99ed9f73fcd3b225bb3cb1a79"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xc770eefad204b5180df6a14ee197d99d808ee52d.png",
|
||||
"symbol": "FOX",
|
||||
"name": "ShapeShift FOX Token",
|
||||
"contractAddress": "0x65a05db8322701724c197af82c9cae41195b0aa8"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0xccBe9B810d6574701d324fD6DbE0A1b68f9d5bf7/logo.png",
|
||||
"symbol": "STACK",
|
||||
"name": "Stacker Ventures",
|
||||
"contractAddress": "0xccbe9b810d6574701d324fd6dbe0a1b68f9d5bf7"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x3a9A81d576d83FF21f26f325066054540720fC34/logo.png",
|
||||
"symbol": "DATA",
|
||||
"name": "Streamr",
|
||||
"contractAddress": "0x3a9a81d576d83ff21f26f325066054540720fc34"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xe53ec727dbdeb9e2d5456c3be40cff031ab40a55.png",
|
||||
"symbol": "SUPER",
|
||||
"name": "SuperFarm",
|
||||
"contractAddress": "0xa1428174f516f527fafdd146b883bb4428682737"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xea319e87cf06203dae107dd8e5672175e3ee976c.png",
|
||||
"symbol": "SURF",
|
||||
"name": "Surf.Finance",
|
||||
"contractAddress": "0x1e42edbe5376e717c1b22904c59e406426e8173f"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/sushi.svg",
|
||||
"symbol": "SUSHI",
|
||||
"name": "Sushi",
|
||||
"contractAddress": "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f.png",
|
||||
"symbol": "SNX",
|
||||
"name": "Synthetix Network Token",
|
||||
"contractAddress": "0x50b728d8d964fd00c2d0aad81718b71311fef68a"
|
||||
},
|
||||
{
|
||||
"decimalCount": 2,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/tel.svg",
|
||||
"symbol": "TEL",
|
||||
"name": "Telcoin",
|
||||
"contractAddress": "0xdf7837de1f2fa4631d716cf2502f8b230f1dcc32"
|
||||
},
|
||||
{
|
||||
"decimalCount": 6,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/usdt.svg",
|
||||
"symbol": "USDT",
|
||||
"name": "Tether",
|
||||
"contractAddress": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xc944e90c64b2c07662a292be6244bdf05cda44a7.png",
|
||||
"symbol": "GRT",
|
||||
"name": "The Graph",
|
||||
"contractAddress": "0x5fe2b58c013d7601147dcdd68c143a77499f5531"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x0000000000085d4780b73119b644ae5ecd22b376.png",
|
||||
"symbol": "TUSD",
|
||||
"name": "TrueUSD",
|
||||
"contractAddress": "0x2e1ad108ff1d8c782fcbbb89aad783ac49586756"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xcc4304a31d09258b0029ea7fe63d032f52e44efe.png",
|
||||
"symbol": "SWAP",
|
||||
"name": "Trustswap",
|
||||
"contractAddress": "0x3809dcdd5dde24b37abe64a5a339784c3323c44f"
|
||||
},
|
||||
{
|
||||
"decimalCount": 6,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/usdc.svg",
|
||||
"symbol": "USDC",
|
||||
"name": "USD Coin",
|
||||
"contractAddress": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x0202be363b8a4820f3f4de7faf5224ff05943ab1.png",
|
||||
"symbol": "UFT",
|
||||
"name": "UniLend Finance",
|
||||
"contractAddress": "0x5b4cf2c120a9702225814e18543ee658c5f8631e"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xf2f9a7e93f845b3ce154efbeb64fb9346fcce509.png",
|
||||
"symbol": "POWER",
|
||||
"name": "UniPower",
|
||||
"contractAddress": "0x00d5149cdf7cec8725bf50073c51c4fa58ecca12"
|
||||
},
|
||||
{
|
||||
"decimalCount": 8,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x8400d94a5cb0fa0d041a3788e395285d61c9ee5e.png",
|
||||
"symbol": "UBT",
|
||||
"name": "Unibright",
|
||||
"contractAddress": "0x7fbc10850cae055b27039af31bd258430e714c62"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/uni.svg",
|
||||
"symbol": "UNI",
|
||||
"name": "Uniswap",
|
||||
"contractAddress": "0xb33eaad8d922b1083446dc23f610c2567fb5180f"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x1b40183EFB4Dd766f11bDa7A7c3AD8982e998421.png",
|
||||
"symbol": "VSP",
|
||||
"name": "Vesper Finance",
|
||||
"contractAddress": "0x09c5a4bca808bd1ba2b8e6b3aaf7442046b4ca5b"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/eth.svg",
|
||||
"symbol": "WETH",
|
||||
"name": "WETH",
|
||||
"contractAddress": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x4691937a7508860f876c9c0a2a617e7d9e945d4b.png",
|
||||
"symbol": "WOO",
|
||||
"name": "WOO Network",
|
||||
"contractAddress": "0x1b815d120b3ef02039ee11dc2d33de7aa4a8c603"
|
||||
},
|
||||
{
|
||||
"decimalCount": 8,
|
||||
"customIconUrl": "https://wallet-asset.matic.network/img/tokens/wbtc.svg",
|
||||
"symbol": "WBTC",
|
||||
"name": "Wrapped Bitcoin",
|
||||
"contractAddress": "0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x6e1A19F235bE7ED8E3369eF73b196C07257494DE.png",
|
||||
"symbol": "WFIL",
|
||||
"name": "Wrapped Filecoin",
|
||||
"contractAddress": "0xede1b77c0ccc45bfa949636757cd2ca7ef30137f"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270/logo.png",
|
||||
"symbol": "WMATIC",
|
||||
"name": "Wrapped Matic",
|
||||
"contractAddress": "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xa47c8bf37f92abed4a126bda807a7b7498661acd.png",
|
||||
"symbol": "UST",
|
||||
"name": "Wrapped UST",
|
||||
"contractAddress": "0x692597b009d13c4049a947cab2239b7d6517875f"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xf4cd3d3fda8d7fd6c5a500203e38640a70bf9577.png",
|
||||
"symbol": "YF-DAI",
|
||||
"name": "YfDAI.finance",
|
||||
"contractAddress": "0x7e7ff932fab08a0af569f93ce65e7b8b23698ad8"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/assets/0x8C92e38eCA8210f4fcBf17F0951b198Dd7668292/logo.png",
|
||||
"symbol": "DHT",
|
||||
"name": "dHEDGE DAO",
|
||||
"contractAddress": "0x8c92e38eca8210f4fcbf17f0951b198dd7668292"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xa0246c9032bc3a600820415ae600c6388619a14d.png",
|
||||
"symbol": "IFARM",
|
||||
"name": "iFARM",
|
||||
"contractAddress": "0xab0b2ddb9c7e440fac8e140a89c0dbcbf2d7bbff"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xa3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2.png",
|
||||
"symbol": "MTA",
|
||||
"name": "mStable Governance Token: Meta",
|
||||
"contractAddress": "0xf501dd45a1198c2e1b5aef5314a68b9006d842e0"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0xfC40a4F89b410a1b855b5e205064a38fC29F5eb5.png",
|
||||
"symbol": "RUSD",
|
||||
"name": "rUSD",
|
||||
"contractAddress": "0xfc40a4f89b410a1b855b5e205064a38fc29f5eb5"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://raw.githubusercontent.com/plasmadlt/token-list/master/assets/0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e.png",
|
||||
"symbol": "YFI",
|
||||
"name": "yearn.finance",
|
||||
"contractAddress": "0xda537104d6a5edd53c6fbba9a898708e465260b6"
|
||||
}
|
||||
]
|
||||
1
app/src/main/assets/polygon_tokens_testnet.json
Normal file
1
app/src/main/assets/polygon_tokens_testnet.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
1444
app/src/main/assets/solana_tokens.json
Normal file
1444
app/src/main/assets/solana_tokens.json
Normal file
File diff suppressed because it is too large
Load diff
14
app/src/main/assets/solana_tokens_testnet.json
Normal file
14
app/src/main/assets/solana_tokens_testnet.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"symbol": "TCA",
|
||||
"name": "Tangem Coin A",
|
||||
"contractAddress": "22PTNbX31Zuztd6nD82fC8nQdT2hUfWv9XWXKuDkrFqR"
|
||||
},
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"symbol": "TCB",
|
||||
"name": "Tangem Coin B",
|
||||
"contractAddress": "HmSghNPg6KCk711YJA92aPejt8auyFkvmaED6jbHfUs4"
|
||||
}
|
||||
]
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit e80938f7d9ba378a91ac544a9170597e64afa696
|
||||
Subproject commit 42803296690ad6914ec2c59c5fdc3f0fb30f8afc
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
package com.tangem.tap
|
||||
|
||||
import android.app.Application
|
||||
import com.appsflyer.AppsFlyerLib
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.google.firebase.remoteconfig.ktx.remoteConfig
|
||||
import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
|
||||
import com.tangem.Log
|
||||
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
|
||||
import com.tangem.tap.common.analytics.GlobalAnalyticsHandler
|
||||
import com.tangem.tap.common.images.PicassoHelper
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.appReducer
|
||||
|
|
@ -28,9 +30,9 @@ import org.rekotlin.Store
|
|||
import timber.log.Timber
|
||||
|
||||
val store = Store(
|
||||
reducer = ::appReducer,
|
||||
middleware = AppState.getMiddleware(),
|
||||
state = AppState()
|
||||
reducer = ::appReducer,
|
||||
middleware = AppState.getMiddleware(),
|
||||
state = AppState()
|
||||
)
|
||||
lateinit var preferencesStorage: PreferencesStorage
|
||||
lateinit var currenciesRepository: CurrenciesRepository
|
||||
|
|
@ -61,6 +63,8 @@ class TapApplication : Application() {
|
|||
loadConfigs()
|
||||
|
||||
BlockchainSdkRetrofitBuilder.enableNetworkLogging = BuildConfig.DEBUG
|
||||
|
||||
initAppsFlyer()
|
||||
}
|
||||
|
||||
private fun loadConfigs() {
|
||||
|
|
@ -82,4 +86,12 @@ class TapApplication : Application() {
|
|||
|
||||
store.dispatch(GlobalAction.SetFeedbackManager(FeedbackManager(infoHolder, logWriter)))
|
||||
}
|
||||
|
||||
private fun initAppsFlyer() {
|
||||
val devKey = store.state.globalState.configManager?.config?.appsFlyerDevKey ?: return
|
||||
AppsFlyerLib.getInstance().init(devKey, null, this)
|
||||
AppsFlyerLib.getInstance().start(this)
|
||||
val analyticsHandler = GlobalAnalyticsHandler.createDefaultAnalyticHandlers(this)
|
||||
store.dispatch(GlobalAction.SetAnanlyticHandlers(analyticsHandler))
|
||||
}
|
||||
}
|
||||
|
|
@ -79,6 +79,15 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
TransactionDialog.create(state.dialog.dialogData, context)
|
||||
is WalletConnectDialog.PersonalSign ->
|
||||
PersonalSignDialog.create(state.dialog.data, context)
|
||||
is WalletConnectDialog.BnbTransactionDialog ->
|
||||
BnbTransactionDialog.create(
|
||||
data = state.dialog.data,
|
||||
session = state.dialog.session,
|
||||
sessionId = state.dialog.sessionId,
|
||||
cardId = state.dialog.cardId,
|
||||
dAppName = state.dialog.dAppName,
|
||||
context = context
|
||||
)
|
||||
is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context)
|
||||
is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context)
|
||||
is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(context)
|
||||
|
|
|
|||
|
|
@ -4,12 +4,66 @@ enum class AnalyticsEvent(val event: String) {
|
|||
CARD_IS_SCANNED("card_is_scanned"),
|
||||
TRANSACTION_IS_SENT("transaction_is_sent"),
|
||||
READY_TO_SCAN("ready_to_scan"),
|
||||
|
||||
APP_RATING_DISPLAYED("rate_app_warning_displayed"),
|
||||
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"),
|
||||
|
||||
GET_CARD("get_card"),
|
||||
}
|
||||
|
||||
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"),
|
||||
|
||||
SOURCE("source"),
|
||||
}
|
||||
|
||||
enum class GetCardSourceParams(val param: String) {
|
||||
WELCOME("welcome"),
|
||||
ONBOARDING_BUY_MORE("wallet_onboaring_buy_more_cards"),
|
||||
ONBOARDING("wallet_onboarding")
|
||||
}
|
||||
|
||||
object Analytics {
|
||||
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 }
|
||||
|
||||
enum class WcAction { PersonalSign, SignTransaction, SendTransaction }
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,75 @@ package com.tangem.tap.common.analytics
|
|||
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.tap.common.extensions.filterNotNull
|
||||
|
||||
|
||||
interface AnalyticsHandler {
|
||||
fun triggerEvent(event: AnalyticsEvent, card: Card? = null, blockchain: String? = null)
|
||||
fun logCardSdkError(
|
||||
abstract class AnalyticsHandler {
|
||||
abstract fun triggerEvent(
|
||||
event: AnalyticsEvent,
|
||||
card: Card? = null,
|
||||
blockchain: String? = null,
|
||||
params: Map<String, String> = emptyMap()
|
||||
)
|
||||
|
||||
abstract fun logCardSdkError(
|
||||
error: TangemSdkError,
|
||||
actionToLog: FirebaseAnalyticsHandler.ActionToLog,
|
||||
parameters: Map<FirebaseAnalyticsHandler.AnalyticsParam, String>? = null,
|
||||
actionToLog: Analytics.ActionToLog,
|
||||
parameters: Map<AnalyticsParam, String>? = null,
|
||||
card: Card? = null
|
||||
)
|
||||
|
||||
abstract fun logError(
|
||||
error: Throwable,
|
||||
params: Map<String, String> = emptyMap()
|
||||
)
|
||||
|
||||
protected fun prepareParams(card: Card?, blockchain: String? = null): Map<String, String> {
|
||||
return mapOf(
|
||||
AnalyticsParam.FIRMWARE.param to card?.firmwareVersion?.stringValue,
|
||||
AnalyticsParam.BATCH_ID.param to card?.batchId,
|
||||
AnalyticsParam.BLOCKCHAIN.param to blockchain,
|
||||
).filterNotNull()
|
||||
}
|
||||
|
||||
fun logWcEvent(event: Analytics.WcAnalyticsEvent) {
|
||||
when (event) {
|
||||
is Analytics.WcAnalyticsEvent.Action -> {
|
||||
triggerEvent(
|
||||
event = AnalyticsEvent.WC_SUCCESS_RESPONSE,
|
||||
params = mapOf(
|
||||
AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action.name
|
||||
)
|
||||
)
|
||||
}
|
||||
is Analytics.WcAnalyticsEvent.Error -> {
|
||||
val params = mapOf(
|
||||
AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action?.name,
|
||||
AnalyticsParam.ERROR_DESCRIPTION.param to event.error.message
|
||||
)
|
||||
.filterNotNull()
|
||||
logError(event.error, params)
|
||||
}
|
||||
is Analytics.WcAnalyticsEvent.InvalidRequest ->
|
||||
triggerEvent(
|
||||
event = AnalyticsEvent.WC_INVALID_REQUEST,
|
||||
params = mapOf(
|
||||
AnalyticsParam.WALLET_CONNECT_REQUEST.param to event.json
|
||||
).filterNotNull()
|
||||
)
|
||||
is Analytics.WcAnalyticsEvent.Session -> {
|
||||
val analyticsEvent = when (event.event) {
|
||||
Analytics.WcSessionEvent.Disconnect -> AnalyticsEvent.WC_SESSION_DISCONNECTED
|
||||
Analytics.WcSessionEvent.Connect -> AnalyticsEvent.WC_NEW_SESSION
|
||||
}
|
||||
triggerEvent(
|
||||
event = analyticsEvent,
|
||||
params = mapOf(
|
||||
AnalyticsParam.WALLET_CONNECT_DAPP_URL.param to event.url
|
||||
).filterNotNull()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import android.app.Application
|
||||
import com.appsflyer.AppsFlyerLib
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
|
||||
class AppsFlyerAnalyticsHandler(val context: Application): AnalyticsHandler() {
|
||||
|
||||
override fun triggerEvent(
|
||||
event: AnalyticsEvent,
|
||||
card: Card?,
|
||||
blockchain: String?,
|
||||
params: Map<String, String>
|
||||
) {
|
||||
AppsFlyerLib.getInstance().logEvent(context,
|
||||
event.event, prepareParams(card, blockchain))
|
||||
}
|
||||
|
||||
override fun logCardSdkError(
|
||||
error: TangemSdkError,
|
||||
actionToLog: Analytics.ActionToLog,
|
||||
parameters: Map<AnalyticsParam, String>?,
|
||||
card: Card?
|
||||
) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
override fun logError(error: Throwable, params: Map<String, String>) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,11 +7,16 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|||
import com.google.firebase.ktx.Firebase
|
||||
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?, blockchain: String?) {
|
||||
Firebase.analytics.logEvent(event.event, setData(card, blockchain))
|
||||
object FirebaseAnalyticsHandler : AnalyticsHandler() {
|
||||
override fun triggerEvent(
|
||||
event: AnalyticsEvent,
|
||||
card: Card?,
|
||||
blockchain: String?,
|
||||
params: Map<String, String>
|
||||
) {
|
||||
val fullParams = prepareParams(card, blockchain) + params
|
||||
Firebase.analytics.logEvent(event.event, fullParams.toBundle())
|
||||
}
|
||||
|
||||
fun logException(name: String, throwable: Throwable) {
|
||||
|
|
@ -22,16 +27,20 @@ object FirebaseAnalyticsHandler : AnalyticsHandler {
|
|||
))
|
||||
}
|
||||
|
||||
private fun Map<String, String>.toBundle(): Bundle {
|
||||
return bundleOf(*this.toList().toTypedArray())
|
||||
}
|
||||
|
||||
override fun logCardSdkError(
|
||||
error: TangemSdkError,
|
||||
actionToLog: ActionToLog,
|
||||
actionToLog: Analytics.ActionToLog,
|
||||
parameters: Map<AnalyticsParam, String>?,
|
||||
card: Card?,
|
||||
) {
|
||||
if (error is TangemSdkError.UserCancelled) return
|
||||
|
||||
val params = parameters?.toMutableMap() ?: mutableMapOf()
|
||||
if (card != null) params + getParamsFromCard(card)
|
||||
if (card != null) params + prepareParams(card)
|
||||
params[AnalyticsParam.ACTION] = actionToLog.key
|
||||
params[AnalyticsParam.ERROR_CODE] = error.code.toString()
|
||||
params[AnalyticsParam.ERROR_DESCRIPTION] = error.javaClass.simpleName
|
||||
|
|
@ -44,100 +53,8 @@ object FirebaseAnalyticsHandler : AnalyticsHandler {
|
|||
FirebaseCrashlytics.getInstance().recordException(cardError)
|
||||
}
|
||||
|
||||
private fun getParamsFromCard(card: Card): Map<AnalyticsParam, String> {
|
||||
return mapOf(
|
||||
AnalyticsParam.FIRMWARE to card.firmwareVersion.stringValue,
|
||||
AnalyticsParam.BATCH_ID to card.batchId
|
||||
).filterNotNull()
|
||||
override fun logError(error: Throwable, params: Map<String, String>) {
|
||||
params.forEach { (key, value) -> FirebaseCrashlytics.getInstance().setCustomKey(key, value) }
|
||||
FirebaseCrashlytics.getInstance().recordException(error)
|
||||
}
|
||||
|
||||
private fun setData(card: Card?, blockchain: String?): Bundle {
|
||||
if (card == null) return bundleOf()
|
||||
return bundleOf(
|
||||
AnalyticsParam.BLOCKCHAIN.param to (blockchain),
|
||||
AnalyticsParam.BATCH_ID.param to card.batchId,
|
||||
AnalyticsParam.FIRMWARE.param to card.firmwareVersion.stringValue
|
||||
)
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import android.app.Application
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
|
||||
class GlobalAnalyticsHandler(val analyticsHandlers: List<AnalyticsHandler>) :
|
||||
AnalyticsHandler() {
|
||||
override fun triggerEvent(
|
||||
event: AnalyticsEvent,
|
||||
card: Card?,
|
||||
blockchain: String?,
|
||||
params: Map<String, String>
|
||||
) {
|
||||
analyticsHandlers.forEach { it.triggerEvent(event, card, blockchain) }
|
||||
}
|
||||
|
||||
override fun logCardSdkError(
|
||||
error: TangemSdkError,
|
||||
actionToLog: Analytics.ActionToLog,
|
||||
parameters: Map<AnalyticsParam, String>?,
|
||||
card: Card?
|
||||
) {
|
||||
analyticsHandlers.forEach { it.logCardSdkError(error, actionToLog, parameters, card) }
|
||||
}
|
||||
|
||||
override fun logError(error: Throwable, params: Map<String, String>) {
|
||||
analyticsHandlers.forEach { it.logError(error, params) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun createDefaultAnalyticHandlers(context: Application): GlobalAnalyticsHandler {
|
||||
return GlobalAnalyticsHandler(
|
||||
listOf(
|
||||
FirebaseAnalyticsHandler,
|
||||
AppsFlyerAnalyticsHandler(context)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,6 +105,7 @@ object TangemSdk {
|
|||
is TangemSdkError.WrongInteractionMode -> TangemSdkError.WrongInteractionMode()
|
||||
is TangemSdkError.IssuerSignatureLoadingFailed -> TangemSdkError.IssuerSignatureLoadingFailed()
|
||||
is TangemSdkError.BackupFailedFirmware -> TangemSdkError.BackupFailedFirmware()
|
||||
is TangemSdkError.UserForgotTheCode -> TangemSdkError.UserForgotTheCode()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
data class ByteArrayKey(val bytes: ByteArray) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return this === other || other is ByteArrayKey && this.bytes.contentEquals(other.bytes)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = bytes.contentHashCode()
|
||||
}
|
||||
|
||||
fun ByteArray.toMapKey(): ByteArrayKey = ByteArrayKey(this)
|
||||
|
||||
fun ByteArrayKey.bytes(): ByteArray = this.bytes
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.net.Uri
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
|
|
@ -9,6 +10,7 @@ import com.squareup.picasso.Picasso
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.IconsUtil
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.tap.domain.extensions.getCustomIconUrl
|
||||
import com.tangem.wallet.R
|
||||
|
||||
fun Picasso.loadCurrenciesIcon(
|
||||
|
|
@ -19,7 +21,7 @@ fun Picasso.loadCurrenciesIcon(
|
|||
) {
|
||||
|
||||
val url = if (token != null) {
|
||||
IconsUtil.getTokenIconUri(blockchain, token)
|
||||
token.getCustomIconUrl()?.let { Uri.parse(it) } ?: IconsUtil.getTokenIconUri(blockchain, token)
|
||||
} else {
|
||||
IconsUtil.getBlockchainIconUri(blockchain)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ fun Store<*>.dispatchOnMain(action: Action) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse, addAnalytics: Boolean = true) {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(scanResponse, addAnalytics)
|
||||
suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse) {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(scanResponse)
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchOpenUrl(url: String) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun StringBuilder.appendIf(value: String, predicate: (String) -> Boolean) {
|
||||
if (predicate(value)) this.append(value)
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
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
|
||||
|
|
@ -26,7 +26,7 @@ suspend fun WalletManager.safeUpdate(): Result<Wallet> = try {
|
|||
val blockchain = wallet.blockchain
|
||||
val amountToCreateAccount = blockchain.amountToCreateAccount(wallet.getFirstToken())
|
||||
|
||||
if (blockchain.isNoAccountError(exception) && amountToCreateAccount != null) {
|
||||
if (exception is BlockchainSdkError.AccountNotFound && amountToCreateAccount != null) {
|
||||
Result.Failure(TapError.WalletManagerUpdate.NoAccountError(amountToCreateAccount.toString()))
|
||||
} else {
|
||||
val message = exception.localizedMessage ?: "An error has occurred. Try later"
|
||||
|
|
|
|||
|
|
@ -1,147 +0,0 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
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,
|
||||
)
|
||||
|
|
@ -1,249 +0,0 @@
|
|||
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.common.extensions.VoidCallback
|
||||
import com.tangem.tangem_sdk_new.extensions.dpToPx
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class LeapfrogWidget(
|
||||
private val parentContainer: FrameLayout,
|
||||
private val calculator: PropertyCalculator = PropertyCalculator(),
|
||||
) {
|
||||
private val foldAnimationDuration = 300L
|
||||
private val leapAnimationDuration = 500L
|
||||
private val leapBackAnimationDuration = 500L
|
||||
|
||||
private val leapViews = mutableListOf<LeapView>()
|
||||
|
||||
private var foldUnfoldProgress: Int = 100
|
||||
private var leapProgress: Int = 100
|
||||
private var leapBackProgress: Int = 100
|
||||
|
||||
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() {
|
||||
leapViews.forEach { it.initView() }
|
||||
}
|
||||
|
||||
fun fold(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
val animator = foldAnimator(animate)
|
||||
if (animator == null) {
|
||||
onEnd()
|
||||
} else {
|
||||
animator.doOnEnd { onEnd() }
|
||||
animator.start()
|
||||
}
|
||||
}
|
||||
|
||||
fun unfold(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
val animator = unfoldAnimator(animate)
|
||||
if (animator == null) {
|
||||
onEnd()
|
||||
} else {
|
||||
animator.doOnEnd { onEnd() }
|
||||
animator.start()
|
||||
}
|
||||
}
|
||||
|
||||
fun foldAnimator(animate: Boolean = true): Animator? {
|
||||
if (!canFoldUnfold()) return null
|
||||
|
||||
return createFoldUnfoldAnimator(true, animate, foldAnimationDuration)
|
||||
}
|
||||
|
||||
fun unfoldAnimator(animate: Boolean = true): Animator? {
|
||||
if (!canFoldUnfold()) return null
|
||||
|
||||
return createFoldUnfoldAnimator(false, animate, foldAnimationDuration)
|
||||
}
|
||||
|
||||
private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, animate: Boolean, duration: Long): Animator {
|
||||
val animatorsList = mutableListOf<Animator>()
|
||||
val animateDuration = if (animate) duration else 0
|
||||
leapViews.forEach {
|
||||
if (isFoldAnimation) {
|
||||
it.fold()
|
||||
animatorsList.add(it.view.foldAnimation(animateDuration, it.state.properties))
|
||||
} else {
|
||||
it.unfold()
|
||||
animatorsList.add(it.view.unfoldAnimation(animateDuration, it.state.properties))
|
||||
}
|
||||
}
|
||||
animatorsList.add(createProgressListener(animateDuration) { foldUnfoldProgress = it })
|
||||
|
||||
val animator = AnimatorSet()
|
||||
animator.interpolator = AccelerateDecelerateInterpolator()
|
||||
animator.playTogether(animatorsList.toList())
|
||||
return animator
|
||||
}
|
||||
|
||||
fun leap(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
if (!canLeap()) {
|
||||
onEnd()
|
||||
return
|
||||
}
|
||||
|
||||
val duration = if (animate) leapAnimationDuration else 0
|
||||
val animatorsList = mutableListOf<Animator>()
|
||||
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(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
if (!canLeapBack()) {
|
||||
onEnd()
|
||||
return
|
||||
}
|
||||
|
||||
val duration = if (animate) leapBackAnimationDuration else 0
|
||||
val animatorsList = mutableListOf<Animator>()
|
||||
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(leapViews.map { it.state })
|
||||
|
||||
fun applyState(state: LeapfrogWidgetState) {
|
||||
state.leapViewStates.forEach { viewState ->
|
||||
leapViews.firstOrNull { it.index == viewState.index }?.applyState(viewState)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
leapBackInProgress() -> false
|
||||
leapProgress < 70 -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
private fun canLeapBack(): Boolean {
|
||||
return when {
|
||||
!viewIsFullFledged() -> 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 leapViewStates: List<LeapViewState>
|
||||
)
|
||||
|
||||
class PropertyCalculator(
|
||||
val elevationFactor: Float = 1f,
|
||||
val decreaseScaleFactor: Float = 0.15f,
|
||||
val yTranslationFactor: Float = 35f,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@ import android.view.View
|
|||
import android.widget.FrameLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidgetState
|
||||
import com.tangem.tap.domain.twins.TwinsCardWidget
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.test_leapfrog_fragment.*
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.tap.common.analytics.GlobalAnalyticsHandler
|
||||
import com.tangem.tap.common.redux.*
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.config.ConfigManager
|
||||
|
|
@ -36,7 +38,7 @@ sealed class GlobalAction : Action {
|
|||
}
|
||||
|
||||
data class ScanCard(
|
||||
val shouldDeriveWC: Boolean,
|
||||
val additionalBlockchainsToDerive: Collection<Blockchain>? = null,
|
||||
val onSuccess: ((ScanResponse) -> Unit)? = null,
|
||||
val onFailure: ((TangemError) -> Unit)? = null,
|
||||
val messageResId: Int? = null,
|
||||
|
|
@ -69,6 +71,7 @@ sealed class GlobalAction : Action {
|
|||
data class SetConfigManager(val configManager: ConfigManager) : GlobalAction()
|
||||
data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction()
|
||||
data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction()
|
||||
data class SetAnanlyticHandlers(val analyticsHandlers: GlobalAnalyticsHandler) : GlobalAction()
|
||||
|
||||
data class SendFeedback(val emailData: EmailData) : GlobalAction()
|
||||
data class UpdateFeedbackInfo(val walletManagers: List<WalletManager>) : GlobalAction()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.*
|
||||
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
|
||||
|
|
@ -89,9 +88,9 @@ private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState
|
|||
is GlobalAction.ScanCard -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.scanProduct(
|
||||
FirebaseAnalyticsHandler,
|
||||
store.state.globalState.analyticsHandlers,
|
||||
currenciesRepository,
|
||||
action.shouldDeriveWC,
|
||||
action.additionalBlockchainsToDerive,
|
||||
action.messageResId
|
||||
)
|
||||
withMainContext {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
|
|||
globalState.copy(configManager = action.configManager)
|
||||
}
|
||||
is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager)
|
||||
is GlobalAction.SetAnanlyticHandlers ->
|
||||
globalState.copy(analyticsHandlers = action.analyticsHandlers)
|
||||
is GlobalAction.UpdateWalletSignedHashes -> {
|
||||
val card = globalState.scanResponse?.card ?: return globalState
|
||||
val wallet = card.wallet(action.walletPublicKey) ?: return globalState
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.tap.common.analytics.AnalyticsHandler
|
||||
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.domain.PayIdManager
|
||||
|
|
@ -28,6 +29,7 @@ data class GlobalState(
|
|||
val dialog: StateDialog? = null,
|
||||
val moonpayStatus: MoonpayStatus? = null,
|
||||
val resources: AndroidResources = AndroidResources(),
|
||||
val analyticsHandlers: AnalyticsHandler? = null,
|
||||
) : StateType
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import CreateProductWalletTaskResponse
|
|||
import android.content.Context
|
||||
import com.tangem.Message
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.CardFilter
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.SuccessResponse
|
||||
|
|
@ -14,24 +15,25 @@ import com.tangem.common.core.CardIdDisplayFormat
|
|||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.Config
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
||||
import com.tangem.operations.pins.CheckUserCodesCommand
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.operations.pins.SetUserCodeCommand
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.AnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.ByteArrayKey
|
||||
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
|
||||
import com.tangem.tap.domain.tasks.DerivationTask
|
||||
import com.tangem.tap.domain.tasks.DerivationTaskResponse
|
||||
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanProductTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.tokens.CurrenciesRepository
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
|
@ -39,16 +41,18 @@ import kotlin.coroutines.suspendCoroutine
|
|||
class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) {
|
||||
|
||||
suspend fun scanProduct(
|
||||
analyticsHandler: AnalyticsHandler,
|
||||
analyticsHandler: AnalyticsHandler?,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
shouldDeriveWC: Boolean,
|
||||
additionalBlockchainsToDerive: Collection<Blockchain>? = null,
|
||||
messageRes: Int? = null,
|
||||
): CompletionResult<ScanResponse> {
|
||||
analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null)
|
||||
analyticsHandler?.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null)
|
||||
|
||||
val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header))
|
||||
return runTaskAsyncReturnOnMain(ScanProductTask(null, currenciesRepository), null, message)
|
||||
.also { sendScanFailuresToAnalytics(analyticsHandler, it) }
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ScanProductTask(null, currenciesRepository, additionalBlockchainsToDerive),
|
||||
cardId = null, initialMessage = message
|
||||
).also { sendScanResultsToAnalytics(analyticsHandler, it) }
|
||||
}
|
||||
|
||||
suspend fun createProductWallet(
|
||||
|
|
@ -61,14 +65,24 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
)
|
||||
}
|
||||
|
||||
private fun sendScanFailuresToAnalytics(
|
||||
analyticsHandler: AnalyticsHandler,
|
||||
private fun sendScanResultsToAnalytics(
|
||||
analyticsHandler: AnalyticsHandler?,
|
||||
result: CompletionResult<ScanResponse>
|
||||
) {
|
||||
if (result is CompletionResult.Failure && result.error is TangemSdkError) {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
analyticsHandler.logCardSdkError(error, FirebaseAnalyticsHandler.ActionToLog.Scan)
|
||||
}
|
||||
when (result) {
|
||||
is CompletionResult.Success ->
|
||||
analyticsHandler?.triggerEvent(
|
||||
event = AnalyticsEvent.CARD_IS_SCANNED,
|
||||
card = result.data.card,
|
||||
blockchain = result.data.walletData?.blockchain
|
||||
)
|
||||
is CompletionResult.Failure ->
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
analyticsHandler?.logCardSdkError(
|
||||
error = error,
|
||||
actionToLog = Analytics.ActionToLog.Scan
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,7 +95,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
cardId: String,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>
|
||||
): CompletionResult<DerivationTaskResponse> {
|
||||
return runTaskAsyncReturnOnMain(DerivationTask(derivations), cardId)
|
||||
return runTaskAsyncReturnOnMain(DeriveMultipleWalletPublicKeysTask(derivations), cardId)
|
||||
}
|
||||
|
||||
suspend fun resetToFactorySettings(card: Card): CompletionResult<Card> {
|
||||
|
|
@ -125,7 +139,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
withContext(Dispatchers.Main) {
|
||||
suspendCoroutine { continuation ->
|
||||
tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result ->
|
||||
continuation.resume(result)
|
||||
if (continuation.context.isActive) continuation.resume(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package com.tangem.tap.domain
|
|||
|
||||
import com.tangem.blockchain.common.*
|
||||
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
|
||||
|
|
@ -71,10 +69,7 @@ class TapWalletManager {
|
|||
handleFiatRatesResult(results)
|
||||
}
|
||||
|
||||
suspend fun onCardScanned(data: ScanResponse, addAnalyticsEvent: Boolean = false) {
|
||||
if (addAnalyticsEvent) {
|
||||
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain)
|
||||
}
|
||||
suspend fun onCardScanned(data: ScanResponse) {
|
||||
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card)
|
||||
updateConfigManager(data)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ data class Config(
|
|||
val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de",
|
||||
val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE",
|
||||
val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C",
|
||||
val appsFlyerDevKey: String = "",
|
||||
val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
|
||||
val isSendingToPayIdEnabled: Boolean = true,
|
||||
val isTopUpEnabled: Boolean = false,
|
||||
|
|
@ -84,7 +85,8 @@ class ConfigManager(
|
|||
blockchairAuthorizationToken = values.blockchairAuthorizationToken,
|
||||
blockcypherTokens = values.blockcypherTokens,
|
||||
infuraProjectId = values.infuraProjectId
|
||||
)
|
||||
),
|
||||
appsFlyerDevKey = values.appsFlyerDevKey,
|
||||
)
|
||||
defaultConfig = defaultConfig.copy(
|
||||
coinMarketCapKey = values.coinMarketCapKey,
|
||||
|
|
@ -95,7 +97,8 @@ class ConfigManager(
|
|||
blockchairAuthorizationToken = values.blockchairAuthorizationToken,
|
||||
blockcypherTokens = values.blockcypherTokens,
|
||||
infuraProjectId = values.infuraProjectId
|
||||
)
|
||||
),
|
||||
appsFlyerDevKey = values.appsFlyerDevKey,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class ConfigValueModel(
|
|||
val blockchairAuthorizationToken: String?,
|
||||
val blockcypherTokens: Set<String>?,
|
||||
val infuraProjectId: String?,
|
||||
val appsFlyerDevKey: String,
|
||||
)
|
||||
|
||||
class ConfigModel(val features: FeatureModel?, val configValues: ConfigValueModel?) {
|
||||
|
|
|
|||
|
|
@ -6,14 +6,6 @@ import com.tangem.common.card.EllipticCurve
|
|||
import java.math.BigDecimal
|
||||
|
||||
|
||||
fun Blockchain.isNoAccountError(exception: Throwable): Boolean {
|
||||
return when (this) {
|
||||
Blockchain.Stellar, Blockchain.XRP ->
|
||||
exception.message?.contains("Account not found") == true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? {
|
||||
return when (this) {
|
||||
Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(1.5) else BigDecimal.ONE
|
||||
|
|
|
|||
|
|
@ -14,10 +14,11 @@ fun MoonpayStatus.buyIsAllowed(currency: Currency): Boolean {
|
|||
|
||||
return when (currency) {
|
||||
is Currency.Blockchain -> {
|
||||
if (currency.blockchain == Blockchain.Unknown || currency.blockchain == Blockchain.BSC) {
|
||||
false
|
||||
} else {
|
||||
availableToBuy.contains(currency.currencySymbol)
|
||||
val blockchain = currency.blockchain
|
||||
when {
|
||||
blockchain.isTestnet() -> blockchain.getTestnetTopUpUrl() != null
|
||||
blockchain == Blockchain.Unknown || blockchain == Blockchain.BSC -> false
|
||||
else -> availableToBuy.contains(currency.currencySymbol)
|
||||
}
|
||||
}
|
||||
is Currency.Token -> false
|
||||
|
|
|
|||
17
app/src/main/java/com/tangem/tap/domain/extensions/Token.kt
Normal file
17
app/src/main/java/com/tangem/tap/domain/extensions/Token.kt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.tap.domain.extensions
|
||||
|
||||
import com.tangem.blockchain.common.Token
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
private val tokenCustomIconUrls = mutableMapOf<String, String>()
|
||||
|
||||
fun Token.getCustomIconUrl(): String? {
|
||||
return tokenCustomIconUrls[this.contractAddress]
|
||||
}
|
||||
|
||||
fun Token.setCustomIconUrl(url: String) {
|
||||
tokenCustomIconUrls[this.contractAddress] = url
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import com.tangem.blockchain.common.WalletManagerFactory
|
|||
import com.tangem.common.card.CardWallet
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.tap.domain.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
|
||||
|
|
@ -33,13 +33,23 @@ fun WalletManagerFactory.makeWalletManagerForApp(
|
|||
}
|
||||
seedKey != null -> {
|
||||
val derivedKeys = scanResponse.derivedKeys[wallet.publicKey.toMapKey()]
|
||||
val derivedKey = derivedKeys?.firstOrNull { it.derivationPath == blockchain.derivationPath() }
|
||||
val derivedKey = derivedKeys?.get(blockchain.derivationPath())
|
||||
?: return null
|
||||
|
||||
makeWalletManager(card.cardId, environmentBlockchain, seedKey, derivedKey)
|
||||
makeWalletManager(
|
||||
cardId = card.cardId,
|
||||
blockchain = environmentBlockchain,
|
||||
seedKey = wallet.publicKey,
|
||||
derivedKey = derivedKey
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
makeWalletManager(card.cardId, environmentBlockchain, wallet.publicKey, wallet.curve)
|
||||
makeWalletManager(
|
||||
cardId = card.cardId,
|
||||
blockchain = environmentBlockchain,
|
||||
walletPublicKey = wallet.publicKey,
|
||||
curve = wallet.curve
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,50 +1,55 @@
|
|||
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.common.hdWallet.DerivationPath
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeyList
|
||||
import com.tangem.tap.common.extensions.ByteArrayKey
|
||||
|
||||
class DerivationTaskResponse(
|
||||
val entries: Map<ByteArrayKey, ExtendedPublicKeyList>
|
||||
): CommandResponse
|
||||
|
||||
class DerivationTask(
|
||||
private val derivations: Map<ByteArrayKey, List<DerivationPath>>
|
||||
) : CardSessionRunnable<DerivationTaskResponse> {
|
||||
|
||||
val response: MutableMap<ByteArrayKey, ExtendedPublicKeyList> = mutableMapOf()
|
||||
|
||||
override fun run(session: CardSession, callback: CompletionCallback<DerivationTaskResponse>) {
|
||||
derive(keys = derivations.keys.toList(), index = 0, session = session, callback = callback)
|
||||
}
|
||||
|
||||
private fun derive(
|
||||
keys: List<ByteArrayKey>,
|
||||
index: Int,
|
||||
session: CardSession,
|
||||
callback: CompletionCallback<DerivationTaskResponse>
|
||||
) {
|
||||
if (index == keys.count()) {
|
||||
callback(CompletionResult.Success(DerivationTaskResponse(response.toMap())))
|
||||
return
|
||||
}
|
||||
|
||||
val key = keys[index]
|
||||
val paths = derivations[key]!!
|
||||
DeriveWalletPublicKeysTask(key.bytes, paths).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
response[key] = result.data
|
||||
derive(keys = keys, index = index + 1, session = session, callback = callback)
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//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.common.hdWallet.DerivationPath
|
||||
//import com.tangem.common.hdWallet.ExtendedPublicKey
|
||||
//import com.tangem.operations.CommandResponse
|
||||
//import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
|
||||
//import com.tangem.tap.common.extensions.ByteArrayKey
|
||||
//
|
||||
//class ExtendedPublicKeyList(
|
||||
// items: Collection<ExtendedPublicKey>
|
||||
//): ArrayList<ExtendedPublicKey>(items), CommandResponse
|
||||
//
|
||||
//
|
||||
//class DerivationTaskResponse(
|
||||
// val entries: Map<ByteArrayKey, ExtendedPublicKeyList>
|
||||
//): CommandResponse
|
||||
//
|
||||
//class DerivationTask(
|
||||
// private val derivations: Map<ByteArrayKey, List<DerivationPath>>
|
||||
//) : CardSessionRunnable<DerivationTaskResponse> {
|
||||
//
|
||||
// val response: MutableMap<ByteArrayKey, ExtendedPublicKeyList> = mutableMapOf()
|
||||
//
|
||||
// override fun run(session: CardSession, callback: CompletionCallback<DerivationTaskResponse>) {
|
||||
// derive(keys = derivations.keys.toList(), index = 0, session = session, callback = callback)
|
||||
// }
|
||||
//
|
||||
// private fun derive(
|
||||
// keys: List<ByteArrayKey>,
|
||||
// index: Int,
|
||||
// session: CardSession,
|
||||
// callback: CompletionCallback<DerivationTaskResponse>
|
||||
// ) {
|
||||
// if (index == keys.count()) {
|
||||
// callback(CompletionResult.Success(DerivationTaskResponse(response.toMap())))
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// val key = keys[index]
|
||||
// val paths = derivations[key]!!
|
||||
// DeriveWalletPublicKeysTask(key.bytes, paths).run(session) { result ->
|
||||
// when (result) {
|
||||
// is CompletionResult.Success -> {
|
||||
// response[key] = result.data[key]
|
||||
// derive(keys = keys, index = index + 1, session = session, callback = callback)
|
||||
// }
|
||||
// is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
@ -6,16 +6,16 @@ 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.common.hdWallet.ExtendedPublicKey
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.backup.StartPrimaryCardLinkingTask
|
||||
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.operations.wallet.CreateWalletTask
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
import com.tangem.tap.domain.ProductType
|
||||
import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
|
||||
import com.tangem.tap.domain.tasks.DerivationTask
|
||||
import com.tangem.tap.domain.tasks.product.CreateWalletsTask
|
||||
import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey
|
||||
import com.tangem.tap.domain.tasks.product.ProductCommandProcessor
|
||||
|
|
@ -24,7 +24,7 @@ import com.tangem.tap.domain.tasks.product.getCurvesForNonCreatedWallets
|
|||
|
||||
data class CreateProductWalletTaskResponse(
|
||||
val card: Card,
|
||||
val derivedKeys: Map<KeyWalletPublicKey, List<ExtendedPublicKey>> = mapOf(),
|
||||
val derivedKeys: Map<KeyWalletPublicKey, ExtendedPublicKeysMap> = mapOf(),
|
||||
val primaryCard: PrimaryCard? = null
|
||||
) : CommandResponse
|
||||
|
||||
|
|
@ -170,7 +170,7 @@ private class CreateWalletTangemWallet : ProductCommandProcessor<CreateProductWa
|
|||
return
|
||||
}
|
||||
|
||||
DerivationTask(mapOf(response.wallet.publicKey.toMapKey() to derivationPaths))
|
||||
DeriveMultipleWalletPublicKeysTask(mapOf(response.wallet.publicKey.toMapKey() to derivationPaths))
|
||||
.run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
|
|
|
|||
|
|
@ -12,19 +12,20 @@ import com.tangem.common.core.CardSession
|
|||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.common.hdWallet.ExtendedPublicKey
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.PreflightReadMode
|
||||
import com.tangem.operations.PreflightReadTask
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.backup.StartPrimaryCardLinkingTask
|
||||
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
|
||||
import com.tangem.tap.common.extensions.ByteArrayKey
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
import com.tangem.tap.domain.ProductType
|
||||
import com.tangem.tap.domain.TapSdkError
|
||||
import com.tangem.tap.domain.TapWorkarounds
|
||||
|
|
@ -33,7 +34,6 @@ import com.tangem.tap.domain.TapWorkarounds.isExcluded
|
|||
import com.tangem.tap.domain.TapWorkarounds.isNotSupportedInThatRelease
|
||||
import com.tangem.tap.domain.extensions.getPrimaryCurve
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tap.domain.tasks.DerivationTask
|
||||
import com.tangem.tap.domain.tokens.CurrenciesRepository
|
||||
import com.tangem.tap.domain.twins.TwinsHelper
|
||||
import com.tangem.tap.preferencesStorage
|
||||
|
|
@ -43,7 +43,7 @@ data class ScanResponse(
|
|||
val productType: ProductType,
|
||||
val walletData: WalletData?,
|
||||
val secondTwinPublicKey: String? = null,
|
||||
val derivedKeys: Map<KeyWalletPublicKey, List<ExtendedPublicKey>> = mapOf(),
|
||||
val derivedKeys: Map<KeyWalletPublicKey, ExtendedPublicKeysMap> = mapOf(),
|
||||
val primaryCard: PrimaryCard? = null
|
||||
) : CommandResponse {
|
||||
|
||||
|
|
@ -80,7 +80,8 @@ typealias KeyWalletPublicKey = ByteArrayKey
|
|||
|
||||
class ScanProductTask(
|
||||
val card: Card? = null,
|
||||
private val currenciesRepository: CurrenciesRepository?
|
||||
private val currenciesRepository: CurrenciesRepository?,
|
||||
private val additionalBlockchainsToDerive: Collection<Blockchain>? = null
|
||||
) : CardSessionRunnable<ScanResponse> {
|
||||
|
||||
override fun run(
|
||||
|
|
@ -101,7 +102,7 @@ class ScanProductTask(
|
|||
val commandProcessor = when {
|
||||
TapWorkarounds.isTangemNote(card) -> ScanNoteProcessor()
|
||||
card.isTangemTwins() -> ScanTwinProcessor()
|
||||
else -> ScanWalletProcessor(currenciesRepository)
|
||||
else -> ScanWalletProcessor(currenciesRepository, additionalBlockchainsToDerive)
|
||||
}
|
||||
commandProcessor.proceed(card, session) { processorResult ->
|
||||
when (processorResult) {
|
||||
|
|
@ -153,7 +154,8 @@ private class ScanNoteProcessor : ProductCommandProcessor<ScanResponse> {
|
|||
}
|
||||
|
||||
private class ScanWalletProcessor(
|
||||
private val currenciesRepository: CurrenciesRepository?
|
||||
private val currenciesRepository: CurrenciesRepository?,
|
||||
private val additionalBlockchainsToDerive: Collection<Blockchain>? = null
|
||||
) : ProductCommandProcessor<ScanResponse> {
|
||||
|
||||
var primaryCard: PrimaryCard? = null
|
||||
|
|
@ -253,7 +255,7 @@ private class ScanWalletProcessor(
|
|||
return
|
||||
}
|
||||
|
||||
DerivationTask(derivations).run(session) { result ->
|
||||
DeriveMultipleWalletPublicKeysTask(derivations).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val response = ScanResponse(
|
||||
|
|
@ -291,6 +293,9 @@ private class ScanWalletProcessor(
|
|||
)
|
||||
)
|
||||
}
|
||||
if (additionalBlockchainsToDerive != null) {
|
||||
blockchainsToDerive.addAll(additionalBlockchainsToDerive)
|
||||
}
|
||||
return blockchainsToDerive.distinct()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ import com.squareup.moshi.Types
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.tap.common.extensions.appendIf
|
||||
import com.tangem.tap.common.extensions.readJsonFileToString
|
||||
import com.tangem.tap.domain.extensions.getCustomIconUrl
|
||||
import com.tangem.tap.domain.extensions.setCustomIconUrl
|
||||
import com.tangem.tap.network.createMoshi
|
||||
|
||||
class CurrenciesRepository(val context: Application) {
|
||||
|
|
@ -60,6 +63,11 @@ class CurrenciesRepository(val context: Application) {
|
|||
saveBlockchains(cardId, blockchains)
|
||||
}
|
||||
|
||||
fun removeCurrencies(cardId: String) {
|
||||
saveTokens(cardId, emptyList())
|
||||
saveBlockchains(cardId, emptyList())
|
||||
}
|
||||
|
||||
private fun loadSavedTokens(cardId: String): List<Token> {
|
||||
val json = try {
|
||||
context.readFileText(getFileNameForTokens(cardId))
|
||||
|
|
@ -70,11 +78,7 @@ class CurrenciesRepository(val context: Application) {
|
|||
return try {
|
||||
tokensAdapter.fromJson(json)!!.map { it.toToken() }.distinct()
|
||||
} catch (exception: Exception) {
|
||||
try {
|
||||
obsoleteTokensAdapter.fromJson(json)!!.map { it.toToken() }
|
||||
} catch (exception: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,49 +110,73 @@ class CurrenciesRepository(val context: Application) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getPopularTokens(isTestNet: Boolean = false): List<Token> {
|
||||
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
|
||||
fun getSupportedTokens(isTestNet: Boolean = false): List<Token> {
|
||||
val supportedTokens = if (isTestNet) {
|
||||
getSupportedTokens().mapNotNull { it.getTestnetVersion() }
|
||||
} else {
|
||||
getSupportedTokens()
|
||||
}
|
||||
|
||||
val ethereumTokensJson = context.assets.readJsonFileToString(ethereumTokensFileName)
|
||||
val bscTokensJson = context.assets.readJsonFileToString(bscTokensFileName)
|
||||
val binanceTokensJson = context.assets.readJsonFileToString(binanceTokensFileName)
|
||||
val blockchainTokens = supportedTokens.map {
|
||||
fromJsonToTokensDao(loadTokensJson(it), it)
|
||||
}
|
||||
val tokens = blockchainTokens.flatten().map { it.toToken() }
|
||||
return tokens
|
||||
}
|
||||
|
||||
return tokensAdapter.fromJson(ethereumTokensJson)!!.map { it.toToken() } +
|
||||
tokensAdapter.fromJson(bscTokensJson)!!.map { it.toToken() } +
|
||||
tokensAdapter.fromJson(binanceTokensJson)!!.mapNotNull {
|
||||
// temporary exclude Binance BEP-8 tokens
|
||||
if (it.type != null && it.type == BINANCE_TOKEN_TYPE_BEP8) null else it.toToken()
|
||||
}
|
||||
private fun loadTokensJson(blockchain: Blockchain): String {
|
||||
val fileName = getFileName(blockchain)
|
||||
return context.assets.readJsonFileToString(fileName)
|
||||
}
|
||||
|
||||
private fun getFileName(blockchain: Blockchain): String {
|
||||
return StringBuilder().apply {
|
||||
append(blockchain.id.toLowerCase().replace("/test", ""))
|
||||
append("_tokens")
|
||||
appendIf("_testnet") { blockchain.isTestnet() }
|
||||
}.toString()
|
||||
}
|
||||
|
||||
private fun fromJsonToTokensDao(tokenJson: String, blockchain: Blockchain): List<TokenDao> {
|
||||
return obsoleteTokensAdapter.fromJson(tokenJson)!!.map { it.toTokenDao(blockchain) }
|
||||
}
|
||||
|
||||
private fun getSupportedTokens(): List<Blockchain> {
|
||||
return listOf(
|
||||
Blockchain.Avalanche,
|
||||
Blockchain.Binance,
|
||||
Blockchain.BSC,
|
||||
Blockchain.Ethereum,
|
||||
Blockchain.Polygon,
|
||||
Blockchain.Solana,
|
||||
)
|
||||
}
|
||||
|
||||
fun getBlockchains(
|
||||
cardFirmware: FirmwareVersion?,
|
||||
cardFirmware: FirmwareVersion,
|
||||
isTestNet: Boolean = false
|
||||
): List<Blockchain> {
|
||||
return if (cardFirmware == null || cardFirmware < FirmwareVersion.MultiWalletAvailable) {
|
||||
val blockchains = if (cardFirmware < FirmwareVersion.MultiWalletAvailable) {
|
||||
Blockchain.secp256k1Blockchains(isTestNet)
|
||||
} else {
|
||||
Blockchain.secp256k1Blockchains(isTestNet) + Blockchain.ed25519OnlyBlockchains(isTestNet)
|
||||
}
|
||||
return excludeUnsupportedBlockchains(blockchains)
|
||||
}
|
||||
|
||||
//TODO: move to the App settings
|
||||
private fun excludeUnsupportedBlockchains(blockchains: List<Blockchain>): List<Blockchain> {
|
||||
return blockchains.toMutableList().apply {
|
||||
removeAll(listOf(
|
||||
Blockchain.Fantom, Blockchain.FantomTestnet
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
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"
|
||||
|
||||
private const val BINANCE_TOKEN_TYPE_BEP8 = "bep8"
|
||||
|
||||
fun getFileNameForTokens(cardId: String): String = "${FILE_NAME_PREFIX_TOKENS}_$cardId"
|
||||
fun getFileNameForBlockchains(cardId: String): String =
|
||||
"${FILE_NAME_PREFIX_BLOCKCHAINS}_$cardId"
|
||||
|
|
@ -163,6 +191,7 @@ data class TokenDao(
|
|||
val decimalCount: Int,
|
||||
@Json(name = "blockchain")
|
||||
val blockchainDao: BlockchainDao,
|
||||
val customIconUrl: String? = null,
|
||||
val type: String? = null
|
||||
) {
|
||||
fun toToken(): Token {
|
||||
|
|
@ -172,7 +201,9 @@ data class TokenDao(
|
|||
contractAddress = contractAddress,
|
||||
decimals = decimalCount,
|
||||
blockchain = blockchainDao.toBlockchain()
|
||||
)
|
||||
).apply {
|
||||
customIconUrl?.let { this.setCustomIconUrl(it) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
@ -182,7 +213,8 @@ data class TokenDao(
|
|||
symbol = token.symbol,
|
||||
contractAddress = token.contractAddress,
|
||||
decimalCount = token.decimals,
|
||||
blockchainDao = BlockchainDao.fromBlockchain(token.blockchain)
|
||||
blockchainDao = BlockchainDao.fromBlockchain(token.blockchain),
|
||||
customIconUrl = token.getCustomIconUrl()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -205,8 +237,7 @@ data class 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)
|
||||
return BlockchainDao(name, blockchain.isTestnet())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -217,13 +248,16 @@ data class ObsoleteTokenDao(
|
|||
val symbol: String,
|
||||
val contractAddress: String,
|
||||
val decimalCount: Int,
|
||||
val customIconUrl: String?,
|
||||
) {
|
||||
fun toToken(): Token {
|
||||
return Token(
|
||||
fun toTokenDao(blockchain: Blockchain): TokenDao {
|
||||
return TokenDao(
|
||||
name = name,
|
||||
symbol = symbol,
|
||||
contractAddress = contractAddress,
|
||||
decimals = decimalCount
|
||||
decimalCount = decimalCount,
|
||||
blockchainDao = BlockchainDao.fromBlockchain(blockchain),
|
||||
customIconUrl = customIconUrl
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,17 @@ import com.tangem.common.core.TangemSdkError
|
|||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.AnalyticsHandler
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.network.createMoshi
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
|
||||
class TwinCardsManager(private val card: Card, assetReader: AssetReader) {
|
||||
class TwinCardsManager(
|
||||
private val card: Card,
|
||||
assetReader: AssetReader,
|
||||
val analyticsHandler: AnalyticsHandler?
|
||||
) {
|
||||
|
||||
private val currentCardId: String = card.cardId
|
||||
|
||||
|
|
@ -31,9 +36,9 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) {
|
|||
is CompletionResult.Success -> currentCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
is CompletionResult.Failure -> {
|
||||
(response.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
analyticsHandler?.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
|
||||
Analytics.ActionToLog.CreateWallet,
|
||||
card = card
|
||||
)
|
||||
}
|
||||
|
|
@ -61,9 +66,9 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) {
|
|||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(response.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
analyticsHandler?.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
|
||||
Analytics.ActionToLog.CreateWallet,
|
||||
card = card
|
||||
)
|
||||
}
|
||||
|
|
@ -81,13 +86,13 @@ class TwinCardsManager(private val card: Card, assetReader: AssetReader) {
|
|||
is CompletionResult.Success -> Result.Success(response.data)
|
||||
is CompletionResult.Failure -> {
|
||||
(response.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
analyticsHandler?.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData,
|
||||
Analytics.ActionToLog.WriteIssuerData,
|
||||
card = card
|
||||
)
|
||||
}
|
||||
Result.failure(response.error)
|
||||
Result.fromTangemSdkError(response.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ import android.animation.PropertyValuesHolder
|
|||
import android.view.View
|
||||
import androidx.core.animation.doOnEnd
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.leapfrogWidget.LeapView
|
||||
import com.tangem.tap.common.leapfrogWidget.LeapViewState
|
||||
import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapView
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapViewState
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
|
||||
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ 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.operations.files.FileHashHelper
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataResponse
|
||||
import com.tangem.operations.issuerAndUserData.WriteIssuerDataCommand
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.github.salomonbrys.kotson.registerTypeAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData
|
||||
import com.tangem.tap.features.details.redux.walletconnect.TradeData
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
|
||||
import com.trustwallet.walletconnect.models.binance.tradeOrderSerializer
|
||||
import timber.log.Timber
|
||||
|
||||
class BnbHelper {
|
||||
companion object {
|
||||
|
||||
fun createMessageData(order: WCBinanceTransferOrder): BinanceMessageData.Transfer {
|
||||
val input = order.msgs.first().inputs.first()
|
||||
val output = order.msgs.first().inputs.first()
|
||||
|
||||
val currency =
|
||||
input.coins.firstOrNull()?.denom ?: Blockchain.Binance
|
||||
val amount = input.coins
|
||||
.mapNotNull { if (it.denom == currency) it.amount else null }
|
||||
.sum()
|
||||
.toBigDecimal()
|
||||
.movePointLeft(Blockchain.Binance.decimals())
|
||||
.stripZeroPlainString()
|
||||
|
||||
val gson = GsonBuilder()
|
||||
.registerTypeAdapter(tradeOrderSerializer)
|
||||
.serializeNulls()
|
||||
.create()
|
||||
|
||||
return BinanceMessageData.Transfer(
|
||||
outputAddress = output.address,
|
||||
amount = amount,
|
||||
address = input.address,
|
||||
data = gson.toJson(order).toByteArray().calculateSha256()
|
||||
)
|
||||
}
|
||||
|
||||
fun createMessageData(order: WCBinanceTradeOrder): BinanceMessageData.Trade {
|
||||
val address = order.msgs.first().sender
|
||||
|
||||
val tradeData = order.msgs.map {
|
||||
val price = it.price.toBigDecimal()
|
||||
.movePointLeft(Blockchain.Binance.decimals())
|
||||
.stripTrailingZeros()
|
||||
val quantity = it.quantity.toBigDecimal()
|
||||
.movePointLeft(Blockchain.Binance.decimals())
|
||||
.stripTrailingZeros()
|
||||
|
||||
val amount = price * quantity
|
||||
val symbol = it.symbol.substringBefore("-")
|
||||
|
||||
TradeData(
|
||||
price = "$price ${Blockchain.Binance.currency}",
|
||||
quantity = "$quantity $symbol",
|
||||
amount = "$amount ${Blockchain.Binance.currency}",
|
||||
symbol = symbol
|
||||
)
|
||||
}
|
||||
val gson = GsonBuilder()
|
||||
.registerTypeAdapter(tradeOrderSerializer)
|
||||
.serializeNulls()
|
||||
.create()
|
||||
val serialized = gson.toJson(order)
|
||||
Timber.d(serialized)
|
||||
|
||||
return BinanceMessageData.Trade(
|
||||
tradeData = tradeData,
|
||||
address = address,
|
||||
data = serialized.toByteArray().calculateSha256()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.github.salomonbrys.kotson.fromJson
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonParser
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.trustwallet.walletconnect.JSONRPC_VERSION
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
|
||||
class EthSignHelper {
|
||||
companion object {
|
||||
private val gson: Gson by lazy {
|
||||
GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.serializeNulls()
|
||||
.create()
|
||||
}
|
||||
|
||||
fun tryToParseEthTypedMessage(data: String): WCEthereumSignMessage? {
|
||||
val request =
|
||||
gson.fromJson<CustomJsonRpcRequests>(data)
|
||||
return if (request.method == WCMethodExtended.ETH_SIGN_TYPE_DATA_V4) {
|
||||
WCEthereumSignMessage(
|
||||
listOf(
|
||||
request.params[0].asString,
|
||||
request.params[1].asString,
|
||||
),
|
||||
WCEthereumSignMessage.WCSignType.TYPED_MESSAGE
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun tryToParseEthTypedMessageString(message: String): String? {
|
||||
return try {
|
||||
val messageString = message
|
||||
.replace("\\", "")
|
||||
.removePrefix("\"")
|
||||
.removeSuffix("\"")
|
||||
val messageJson = JsonParser().parse(messageString)
|
||||
val filteredMap = gson.fromJson<Map<*,*>>(messageJson)
|
||||
.filterKeys { it == "domain" || it == "message"}
|
||||
|
||||
gson.toJson(filteredMap)
|
||||
} catch (exception: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class CustomJsonRpcRequests(
|
||||
val id: Long,
|
||||
val jsonrpc: String = JSONRPC_VERSION,
|
||||
val method: WCMethodExtended?,
|
||||
val params: JsonArray
|
||||
)
|
||||
|
||||
enum class WCMethodExtended {
|
||||
@SerializedName("wc_sessionRequest")
|
||||
SESSION_REQUEST,
|
||||
|
||||
@SerializedName("wc_sessionUpdate")
|
||||
SESSION_UPDATE,
|
||||
|
||||
@SerializedName("eth_sign")
|
||||
ETH_SIGN,
|
||||
|
||||
@SerializedName("personal_sign")
|
||||
ETH_PERSONAL_SIGN,
|
||||
|
||||
@SerializedName("eth_signTypedData")
|
||||
ETH_SIGN_TYPE_DATA,
|
||||
|
||||
@SerializedName("eth_signTypedData_v4")
|
||||
ETH_SIGN_TYPE_DATA_V4,
|
||||
|
||||
@SerializedName("eth_signTransaction")
|
||||
ETH_SIGN_TRANSACTION,
|
||||
|
||||
@SerializedName("eth_sendTransaction")
|
||||
ETH_SEND_TRANSACTION,
|
||||
|
||||
@SerializedName("bnb_sign")
|
||||
BNB_SIGN,
|
||||
|
||||
@SerializedName("bnb_tx_confirmation")
|
||||
BNB_TRANSACTION_CONFIRM,
|
||||
|
||||
@SerializedName("get_accounts")
|
||||
GET_ACCOUNTS,
|
||||
|
||||
@SerializedName("trust_signTransaction")
|
||||
SIGN_TRANSACTION;
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package com.tangem.tap.domain.walletconnect
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.*
|
||||
|
|
@ -11,6 +11,7 @@ import com.tangem.tap.store
|
|||
import com.tangem.tap.walletConnectRepository
|
||||
import com.trustwallet.walletconnect.WCClient
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.binance.*
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
|
|
@ -22,6 +23,7 @@ import okhttp3.logging.HttpLoggingInterceptor
|
|||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.collections.set
|
||||
|
||||
class WalletConnectManager {
|
||||
|
||||
|
|
@ -40,7 +42,7 @@ class WalletConnectManager {
|
|||
|
||||
private var sessions: MutableMap<WCSession, WalletConnectActiveData> = mutableMapOf()
|
||||
|
||||
fun connect(wcUri: String, wallet: WalletForSession) {
|
||||
fun connect(wcUri: String) {
|
||||
val session = WCSession.from(wcUri) ?: return
|
||||
if (sessions[session] != null) {
|
||||
store.dispatchOnMain(WalletConnectAction.RefuseOpeningSession)
|
||||
|
|
@ -55,11 +57,20 @@ class WalletConnectManager {
|
|||
remotePeerId = null,
|
||||
session = session,
|
||||
client = client,
|
||||
wallet = wallet
|
||||
wallet = WalletForSession(cardId = "")
|
||||
)
|
||||
setupConnectionTimeoutCheck(session)
|
||||
}
|
||||
|
||||
fun updateSession(session: WalletConnectSession) {
|
||||
val updatedSession = sessions[session.session]?.copy(
|
||||
wallet = session.wallet
|
||||
)
|
||||
if (updatedSession != null) {
|
||||
sessions[session.session] = updatedSession
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupConnectionTimeoutCheck(session: WCSession) {
|
||||
scope.launch {
|
||||
delay(20_000)
|
||||
|
|
@ -81,7 +92,7 @@ class WalletConnectManager {
|
|||
client = WCClient(httpClient = okHttpClient),
|
||||
session = session.session,
|
||||
peerMeta = session.peerMeta,
|
||||
wallet = session.wallet
|
||||
wallet = session.wallet,
|
||||
)
|
||||
.also {
|
||||
setListeners(it.client)
|
||||
|
|
@ -96,11 +107,12 @@ class WalletConnectManager {
|
|||
val activeData = sessions[session] ?: return
|
||||
removeSimilarSessions(activeData)
|
||||
|
||||
val key = activeData.wallet.derivedPublicKey ?: activeData.wallet.walletPublicKey
|
||||
val accounts = listOf(Blockchain.Ethereum.makeAddresses(key).first().value)
|
||||
val key = activeData.wallet.derivedPublicKey ?: activeData.wallet.walletPublicKey ?: return
|
||||
val blockchain = activeData.wallet.getBlockchainForSession()
|
||||
val accounts = listOf(blockchain.makeAddresses(key).first().value)
|
||||
val approved = activeData.client.approveSession(
|
||||
accounts = accounts,
|
||||
chainId = activeData.wallet.chainId
|
||||
chainId = blockchain.getChainId() ?: Blockchain.Ethereum.getChainId()!!
|
||||
)
|
||||
if (approved) {
|
||||
val walletConnectSession = WalletConnectSession(
|
||||
|
|
@ -111,15 +123,18 @@ class WalletConnectManager {
|
|||
peerMeta = activeData.peerMeta!!
|
||||
)
|
||||
walletConnectRepository.saveSession(walletConnectSession)
|
||||
store.dispatchOnMain(WalletConnectAction.ApproveSession.Success(
|
||||
walletConnectSession))
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.ApproveSession.Success(
|
||||
walletConnectSession
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun removeSimilarSessions(activeData: WalletConnectActiveData) {
|
||||
val sessionsToRemove = sessions.filter {
|
||||
it.value.wallet.walletPublicKey == activeData.wallet.walletPublicKey
|
||||
it.value.wallet.walletPublicKey?.equals(activeData.wallet.walletPublicKey) == true
|
||||
&& it.value.peerMeta?.url == activeData.peerMeta?.url
|
||||
&& it.value.session != activeData.session
|
||||
}
|
||||
|
|
@ -139,16 +154,22 @@ class WalletConnectManager {
|
|||
|
||||
fun disconnect(session: WCSession) {
|
||||
val activeData = sessions[session] ?: return
|
||||
val disconnected = activeData.client.killSession()
|
||||
val disconnected = if (activeData.client.isConnected) {
|
||||
activeData.client.killSession()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
if (disconnected) {
|
||||
onSessionClosed(session)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSessionClosed(session: WCSession) {
|
||||
FirebaseAnalyticsHandler.logWcEvent(
|
||||
FirebaseAnalyticsHandler.WcAnalyticsEvent.Session(
|
||||
FirebaseAnalyticsHandler.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url)
|
||||
store.state.globalState.analyticsHandlers?.logWcEvent(
|
||||
Analytics.WcAnalyticsEvent.Session(
|
||||
Analytics.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url
|
||||
)
|
||||
)
|
||||
sessions.remove(session)
|
||||
walletConnectRepository.removeSession(session)
|
||||
|
|
@ -170,17 +191,23 @@ class WalletConnectManager {
|
|||
type = type
|
||||
).guard {
|
||||
sessions[session.session] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(WalletConnectAction.RejectRequest(
|
||||
session.session,
|
||||
id
|
||||
))
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
session.session,
|
||||
id
|
||||
)
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
sessions[session.session] = activeData.copy(transactionData = data)
|
||||
|
||||
store.dispatchOnMain(WalletConnectAction.SetDataToSend(data))
|
||||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.RequestTransaction(
|
||||
data.dialogData)))
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.RequestTransaction(
|
||||
data.dialogData
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,13 +217,34 @@ class WalletConnectManager {
|
|||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().completeTransaction(data).guard {
|
||||
sessions[data.session.session] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(WalletConnectAction.RejectRequest(data.session.session,
|
||||
data.id
|
||||
))
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
data.session.session,
|
||||
data.id
|
||||
)
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
acceptRequest(data.session.session, data.id, hash)
|
||||
sessions[data.session.session] = activeData.copy(transactionData = null)
|
||||
acceptRequest(session, data.id, hash)
|
||||
sessions[session] = activeData.copy(transactionData = null)
|
||||
}
|
||||
}
|
||||
|
||||
fun signBnb(
|
||||
id: Long, data: ByteArray, sessionData: WCSession,
|
||||
) {
|
||||
val activeData = sessions[sessionData] ?: return
|
||||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().signBnbTransaction(data, activeData).guard {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
sessionData,
|
||||
id
|
||||
)
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
acceptRequest(sessionData, id, hash)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -211,9 +259,13 @@ class WalletConnectManager {
|
|||
message = message, session = session, id = id
|
||||
)
|
||||
sessions[session.session] = activeData.copy(personalSignData = data)
|
||||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.PersonalSign(
|
||||
data.dialogData
|
||||
)))
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.PersonalSign(
|
||||
data.dialogData
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,9 +276,12 @@ class WalletConnectManager {
|
|||
val hash = WalletConnectSdkHelper().signPersonalMessage(data.hash, activeData.wallet)
|
||||
.guard {
|
||||
sessions[data.session.session] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(WalletConnectAction.RejectRequest(data.session.session,
|
||||
data.id
|
||||
))
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
data.session.session,
|
||||
data.id
|
||||
)
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
sessions[session] = activeData.copy(personalSignData = data)
|
||||
|
|
@ -245,12 +300,17 @@ class WalletConnectManager {
|
|||
sessions[session] = data
|
||||
val sessionData = data.toWalletConnectSession()
|
||||
sessionData?.let {
|
||||
store.dispatchOnMain(WalletConnectAction.AcceptOpeningSession(
|
||||
sessionData))
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.ScanCard(
|
||||
session = sessionData,
|
||||
chainId = client.chainId?.toIntOrNull()
|
||||
)
|
||||
)
|
||||
}
|
||||
FirebaseAnalyticsHandler.logWcEvent(
|
||||
FirebaseAnalyticsHandler.WcAnalyticsEvent.Session(
|
||||
FirebaseAnalyticsHandler.WcSessionEvent.Connect, peer.url)
|
||||
store.state.globalState.analyticsHandlers?.logWcEvent(
|
||||
Analytics.WcAnalyticsEvent.Session(
|
||||
Analytics.WcSessionEvent.Connect, peer.url
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -261,56 +321,113 @@ class WalletConnectManager {
|
|||
}
|
||||
client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction ->
|
||||
Timber.d("onEthSendTransaction: $transaction")
|
||||
FirebaseAnalyticsHandler.logWcEvent(
|
||||
FirebaseAnalyticsHandler.WcAnalyticsEvent.Action(
|
||||
FirebaseAnalyticsHandler.WcAction.SendTransaction
|
||||
store.state.globalState.analyticsHandlers?.logWcEvent(
|
||||
Analytics.WcAnalyticsEvent.Action(
|
||||
Analytics.WcAction.SendTransaction
|
||||
)
|
||||
)
|
||||
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(WalletConnectAction.HandleTransactionRequest(
|
||||
transaction = transaction,
|
||||
session = sessionData,
|
||||
id = id,
|
||||
type = WcTransactionType.EthSendTransaction
|
||||
))
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandleTransactionRequest(
|
||||
transaction = transaction,
|
||||
session = sessionData,
|
||||
id = id,
|
||||
type = WcTransactionType.EthSendTransaction
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction ->
|
||||
Timber.d("onEthSignTransaction: $transaction")
|
||||
FirebaseAnalyticsHandler.logWcEvent(
|
||||
FirebaseAnalyticsHandler.WcAnalyticsEvent.Action(
|
||||
FirebaseAnalyticsHandler.WcAction.SignTransaction
|
||||
store.state.globalState.analyticsHandlers?.logWcEvent(
|
||||
Analytics.WcAnalyticsEvent.Action(
|
||||
Analytics.WcAction.SignTransaction
|
||||
)
|
||||
)
|
||||
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(WalletConnectAction.HandleTransactionRequest(
|
||||
transaction = transaction,
|
||||
session = sessionData,
|
||||
id = id,
|
||||
type = WcTransactionType.EthSignTransaction
|
||||
))
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandleTransactionRequest(
|
||||
transaction = transaction,
|
||||
session = sessionData,
|
||||
id = id,
|
||||
type = WcTransactionType.EthSignTransaction
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onEthSign = { id: Long, message: WCEthereumSignMessage ->
|
||||
Timber.d("onEthSign: $message")
|
||||
FirebaseAnalyticsHandler.logWcEvent(
|
||||
FirebaseAnalyticsHandler.WcAnalyticsEvent.Action(
|
||||
FirebaseAnalyticsHandler.WcAction.PersonalSign
|
||||
store.state.globalState.analyticsHandlers?.logWcEvent(
|
||||
Analytics.WcAnalyticsEvent.Action(
|
||||
Analytics.WcAction.PersonalSign
|
||||
)
|
||||
)
|
||||
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(WalletConnectAction.HandlePersonalSignRequest(
|
||||
message,
|
||||
sessionData,
|
||||
id))
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandlePersonalSignRequest(
|
||||
message,
|
||||
sessionData,
|
||||
id
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
client.onBnbCancel = { id: Long, order: WCBinanceCancelOrder ->
|
||||
|
||||
}
|
||||
client.onBnbTrade = { id: Long, order: WCBinanceTradeOrder ->
|
||||
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.BinanceTransaction.Trade(
|
||||
id = id, order = order, sessionData = sessionData
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onBnbTransfer = { id: Long, order: WCBinanceTransferOrder ->
|
||||
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.BinanceTransaction.Transfer(
|
||||
id = id, order = order, sessionData = sessionData
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onBnbTxConfirm = { id: Long, order: WCBinanceTxConfirmParam ->
|
||||
// send empty approve request if status is OK
|
||||
if (order.ok) client.approveRequest(id, "")
|
||||
}
|
||||
client.onDisconnect = { code: Int, reason: String ->
|
||||
val session = client.session
|
||||
if (session != null) {
|
||||
onSessionClosed(session)
|
||||
}
|
||||
}
|
||||
client.onCustomRequest = { id: Long, data: String ->
|
||||
Timber.d("Custom Request")
|
||||
Timber.d(data)
|
||||
|
||||
val message = EthSignHelper.tryToParseEthTypedMessage(data)
|
||||
if (message != null) {
|
||||
Timber.d("onEthSign_v4: $message")
|
||||
store.state.globalState.analyticsHandlers?.logWcEvent(
|
||||
Analytics.WcAnalyticsEvent.Action(
|
||||
Analytics.WcAction.PersonalSign
|
||||
)
|
||||
)
|
||||
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandlePersonalSignRequest(
|
||||
message,
|
||||
sessionData,
|
||||
id
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
|
||||
class WalletConnectNetworkUtils {
|
||||
|
||||
companion object {
|
||||
|
||||
fun parseBlockchain(
|
||||
chainId: Int?,
|
||||
peer: WCPeerMeta,
|
||||
): Blockchain? {
|
||||
return when {
|
||||
chainId != null -> {
|
||||
Blockchain.fromChainId(chainId)
|
||||
}
|
||||
peer.url.contains("matic.network") || peer.name == "Polygon" -> {
|
||||
Blockchain.Polygon
|
||||
}
|
||||
peer.url.contains("binance.org") || peer.name.contains("Binance") -> {
|
||||
if (peer.icons.firstOrNull()?.contains("testnet") == true) {
|
||||
Blockchain.BinanceTestnet
|
||||
} else {
|
||||
Blockchain.Binance
|
||||
}
|
||||
}
|
||||
peer.name.contains("BSC") -> {
|
||||
Blockchain.BSC
|
||||
}
|
||||
else -> {
|
||||
Blockchain.Ethereum
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +88,6 @@ data class SessionDao(
|
|||
session = session.session,
|
||||
peerMeta = session.peerMeta
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,9 +10,11 @@ import com.tangem.blockchain.extensions.*
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toDecompressedPublicKey
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.crypto.CryptoUtils
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
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
|
||||
|
|
@ -33,20 +35,8 @@ class WalletConnectSdkHelper {
|
|||
id: Long,
|
||||
type: WcTransactionType,
|
||||
): WcTransactionData? {
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
val publicKey = Wallet.PublicKey(
|
||||
session.wallet.walletPublicKey,
|
||||
session.wallet.derivedPublicKey,
|
||||
session.wallet.derivationPath,
|
||||
)
|
||||
val walletManager = factory.makeEthereumWalletManager(
|
||||
session.wallet.cardId,
|
||||
publicKey,
|
||||
emptyList(),
|
||||
isTestNet = session.wallet.isTestNet
|
||||
) ?: return null
|
||||
|
||||
|
||||
val walletManager = getWalletManager(session) ?: return null
|
||||
try {
|
||||
walletManager.update()
|
||||
} catch (exception: Exception) {
|
||||
|
|
@ -54,15 +44,15 @@ class WalletConnectSdkHelper {
|
|||
return null
|
||||
}
|
||||
|
||||
val blockchain = walletManager.wallet.blockchain
|
||||
|
||||
val wallet = walletManager.wallet
|
||||
val balance =
|
||||
walletManager.wallet.amounts[AmountType.Coin]?.value ?: return null
|
||||
wallet.amounts[AmountType.Coin]?.value ?: return null
|
||||
|
||||
val gas = transaction.gas?.hexToBigDecimal()
|
||||
?: transaction.gasLimit?.hexToBigDecimal() ?: return null
|
||||
?: transaction.gasLimit?.hexToBigDecimal()
|
||||
?: BigDecimal(300000) //Set high gasLimit if not provided
|
||||
|
||||
val decimals = blockchain.decimals()
|
||||
val decimals = wallet.blockchain.decimals()
|
||||
|
||||
val value = (transaction.value ?: "0").hexToBigDecimal()
|
||||
?.movePointLeft(decimals) ?: return null
|
||||
|
|
@ -82,8 +72,8 @@ class WalletConnectSdkHelper {
|
|||
val total = value + fee
|
||||
|
||||
val transactionData = TransactionData(
|
||||
amount = Amount(value, blockchain),
|
||||
fee = Amount(fee, blockchain),
|
||||
amount = Amount(value, wallet.blockchain),
|
||||
fee = Amount(fee, wallet.blockchain),
|
||||
sourceAddress = transaction.from,
|
||||
destinationAddress = transaction.to!!,
|
||||
extras = EthereumTransactionExtras(
|
||||
|
|
@ -115,6 +105,21 @@ class WalletConnectSdkHelper {
|
|||
)
|
||||
}
|
||||
|
||||
private fun getWalletManager(session: WalletConnectSession): WalletManager? {
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
val publicKey = Wallet.PublicKey(
|
||||
session.wallet.walletPublicKey ?: return null,
|
||||
session.wallet.derivedPublicKey,
|
||||
session.wallet.derivationPath,
|
||||
)
|
||||
val blockchain = session.wallet.getBlockchainForSession()
|
||||
return factory.makeWalletManager(
|
||||
session.wallet.cardId,
|
||||
blockchain,
|
||||
publicKey
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun completeTransaction(data: WcTransactionData): String? {
|
||||
return when (data.type) {
|
||||
WcTransactionType.EthSendTransaction -> sendTransaction(data)
|
||||
|
|
@ -133,9 +138,9 @@ class WalletConnectSdkHelper {
|
|||
}
|
||||
is SimpleResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.WalletConnectTransaction,
|
||||
Analytics.ActionToLog.WalletConnectTransaction,
|
||||
)
|
||||
}
|
||||
Timber.e(result.error)
|
||||
|
|
@ -164,9 +169,38 @@ class WalletConnectSdkHelper {
|
|||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.WalletConnectSign,
|
||||
Analytics.ActionToLog.WalletConnectSign,
|
||||
)
|
||||
}
|
||||
Timber.e(result.error.customMessage)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun signBnbTransaction(data: ByteArray, session: WalletConnectActiveData): String? {
|
||||
val command = SignHashCommand(
|
||||
hash = data,
|
||||
walletPublicKey = session.wallet.walletPublicKey ?: return null,
|
||||
derivationPath = session.wallet.derivationPath
|
||||
)
|
||||
val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message())
|
||||
return when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val key = session.wallet.derivedPublicKey?.toDecompressedPublicKey()
|
||||
?: session.wallet.walletPublicKey.toDecompressedPublicKey()
|
||||
getBnbResultString(
|
||||
key.toHexString(),
|
||||
result.data.signature.toHexString()
|
||||
)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error,
|
||||
Analytics.ActionToLog.WalletConnectTransaction,
|
||||
)
|
||||
}
|
||||
Timber.e(result.error.customMessage)
|
||||
|
|
@ -180,8 +214,15 @@ class WalletConnectSdkHelper {
|
|||
session: WalletConnectSession,
|
||||
id: Long,
|
||||
): WcPersonalSignData {
|
||||
val messageData = message.data.removePrefix(HEX_PREFIX).hexToBytes()
|
||||
val messageString = message.data.hexToAscii() ?: message.data
|
||||
val messageData =
|
||||
try {
|
||||
message.data.removePrefix(HEX_PREFIX).hexToBytes()
|
||||
} catch (exception: Exception) {
|
||||
message.data.asciiToHex()!!.hexToBytes()
|
||||
}
|
||||
val messageString = message.data.hexToAscii()
|
||||
?: EthSignHelper.tryToParseEthTypedMessageString(message.data)
|
||||
?: message.data
|
||||
|
||||
val prefixData = (ETH_MESSAGE_PREFIX + messageData.size.toString()).toByteArray()
|
||||
val hashToSign = (prefixData + messageData).toKeccak()
|
||||
|
|
@ -203,29 +244,40 @@ class WalletConnectSdkHelper {
|
|||
}
|
||||
|
||||
private fun String.hexToAscii(): String? {
|
||||
return removePrefix(HEX_PREFIX).hexToBytes()
|
||||
.map {
|
||||
val char = it.toInt().toChar()
|
||||
if (char.isAscii()) char else return null
|
||||
}
|
||||
.joinToString("")
|
||||
return try {
|
||||
removePrefix(HEX_PREFIX).hexToBytes()
|
||||
.map {
|
||||
val char = it.toInt().toChar()
|
||||
if (char.isAscii()) char else return null
|
||||
}
|
||||
.joinToString("")
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.asciiToHex(): String? {
|
||||
return map {
|
||||
if (!it.isAscii()) return null
|
||||
Integer.toHexString(it.code)
|
||||
}.joinToString("")
|
||||
}
|
||||
|
||||
suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? {
|
||||
val key = wallet.derivedPublicKey ?: wallet.walletPublicKey
|
||||
val command = SignHashCommand(hashToSign, wallet.walletPublicKey, wallet.derivationPath)
|
||||
val command = SignHashCommand(hashToSign, wallet.walletPublicKey!!, wallet.derivationPath)
|
||||
return when (val result = tangemSdkManager.runTaskAsync(command, wallet.cardId)) {
|
||||
is CompletionResult.Success -> {
|
||||
val hash = result.data.signature
|
||||
return EthereumUtils.prepareSignedMessageData(
|
||||
hash, hashToSign, CryptoUtils.decompressPublicKey(key)
|
||||
hash, hashToSign, CryptoUtils.decompressPublicKey(key!!)
|
||||
)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.WalletConnectSign,
|
||||
Analytics.ActionToLog.WalletConnectSign,
|
||||
)
|
||||
}
|
||||
Timber.e(result.error.customMessage)
|
||||
|
|
@ -237,5 +289,8 @@ class WalletConnectSdkHelper {
|
|||
companion object {
|
||||
private const val ETH_MESSAGE_PREFIX = "\u0019Ethereum Signed Message:\n"
|
||||
private const val HEX_PREFIX = "0x"
|
||||
fun getBnbResultString(publicKey: String, signature: String): String {
|
||||
return "{\"signature\":\"$signature\",\"publicKey\":\"$publicKey\"}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,9 @@ import com.tangem.common.card.FirmwareVersion
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.AnalyticsParam
|
||||
import com.tangem.tap.common.extensions.dispatchNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -18,10 +20,6 @@ import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
|||
import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.network.coinmarketcap.CoinMarketCapService
|
||||
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
|
||||
|
|
@ -114,13 +112,14 @@ class DetailsMiddleware {
|
|||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
currenciesRepository.removeCurrencies(card.cardId)
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.PurgeWallet,
|
||||
Analytics.ActionToLog.PurgeWallet,
|
||||
card = store.state.detailsState.scanResponse?.card
|
||||
)
|
||||
}
|
||||
|
|
@ -206,11 +205,11 @@ class DetailsMiddleware {
|
|||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error = error,
|
||||
actionToLog = FirebaseAnalyticsHandler.ActionToLog.ChangeSecOptions,
|
||||
actionToLog = Analytics.ActionToLog.ChangeSecOptions,
|
||||
parameters = mapOf(
|
||||
FirebaseAnalyticsHandler.AnalyticsParam.NEW_SECURITY_OPTION to
|
||||
AnalyticsParam.NEW_SECURITY_OPTION to
|
||||
(selectedOption?.name ?: "")
|
||||
),
|
||||
card = store.state.detailsState.scanResponse?.card
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ package com.tangem.tap.features.details.redux.walletconnect
|
|||
|
||||
import android.app.Activity
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.wallet.R
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
|
|
@ -20,20 +23,23 @@ sealed class WalletConnectAction : Action {
|
|||
|
||||
data class ShowClipboardOrScanQrDialog(val wcUri: String) : WalletConnectAction()
|
||||
|
||||
data class ScanCard(val wcUri: String) : WalletConnectAction()
|
||||
|
||||
object UnsupportedCard : WalletConnectAction()
|
||||
|
||||
data class OpenSession(
|
||||
val wcUri: String,
|
||||
val wallet: WalletForSession,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class AddScanResponse(
|
||||
val scanResponse: ScanResponse
|
||||
): WalletConnectAction()
|
||||
|
||||
object RefuseOpeningSession : WalletConnectAction()
|
||||
|
||||
data class OpeningSessionTimeout(val session: WCSession) : WalletConnectAction()
|
||||
|
||||
data class AcceptOpeningSession(val session: WalletConnectSession) : WalletConnectAction()
|
||||
data class ScanCard(
|
||||
val session: WalletConnectSession, val chainId: Int?,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class ApproveSession(
|
||||
val session: WCSession,
|
||||
|
|
@ -79,4 +85,20 @@ sealed class WalletConnectAction : Action {
|
|||
object NotifyCameraPermissionIsRequired : WalletConnectAction(), NotificationAction {
|
||||
override val messageResource = R.string.common_camera_denied_alert_message
|
||||
}
|
||||
|
||||
object BinanceTransaction : WalletConnectAction() {
|
||||
data class Trade(
|
||||
val id: Long, val order: WCBinanceTradeOrder,
|
||||
val sessionData: WalletConnectSession,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class Transfer(
|
||||
val id: Long, val order: WCBinanceTransferOrder,
|
||||
val sessionData: WalletConnectSession,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class Sign(
|
||||
val id: Long, val data: ByteArray, val sessionData: WCSession,
|
||||
) : WalletConnectAction()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,24 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.tap.*
|
||||
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.currenciesRepository
|
||||
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.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.walletconnect.BnbHelper
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectManager
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
|
|
@ -33,7 +37,7 @@ class WalletConnectMiddleware {
|
|||
is WalletConnectAction.HandleDeepLink -> {
|
||||
if (!action.wcUri.isNullOrBlank()) {
|
||||
if (WalletConnectManager.isCorrectWcUri(action.wcUri)) {
|
||||
store.dispatchOnMain(WalletConnectAction.ScanCard(action.wcUri))
|
||||
store.dispatchOnMain(WalletConnectAction.OpenSession(action.wcUri))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,12 +52,13 @@ class WalletConnectMiddleware {
|
|||
}
|
||||
|
||||
is WalletConnectAction.ShowClipboardOrScanQrDialog -> {
|
||||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.ClipboardOrScanQr(
|
||||
action.wcUri)))
|
||||
}
|
||||
|
||||
is WalletConnectAction.ScanCard -> {
|
||||
handleScanCard(action.wcUri)
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.ClipboardOrScanQr(
|
||||
action.wcUri
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is WalletConnectAction.OpeningSessionTimeout -> {
|
||||
|
|
@ -73,19 +78,19 @@ class WalletConnectMiddleware {
|
|||
is WalletConnectAction.OpenSession -> {
|
||||
walletConnectManager.connect(
|
||||
wcUri = action.wcUri,
|
||||
wallet = action.wallet
|
||||
)
|
||||
}
|
||||
|
||||
is WalletConnectAction.RefuseOpeningSession -> {
|
||||
store.dispatch(GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.OpeningSessionRejected
|
||||
))
|
||||
store.dispatch(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.OpeningSessionRejected
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is WalletConnectAction.AcceptOpeningSession -> {
|
||||
store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.ApproveWcSession(
|
||||
action.session)))
|
||||
is WalletConnectAction.ScanCard -> {
|
||||
scanCard(action.session, action.chainId)
|
||||
}
|
||||
|
||||
is WalletConnectAction.ApproveSession -> {
|
||||
|
|
@ -120,74 +125,135 @@ class WalletConnectMiddleware {
|
|||
is WalletConnectAction.SignMessage -> {
|
||||
walletConnectManager.sendSignedMessage(action.session)
|
||||
}
|
||||
is WalletConnectAction.BinanceTransaction.Trade -> {
|
||||
val messageData = BnbHelper.createMessageData(action.order)
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.BnbTransactionDialog(
|
||||
data = messageData,
|
||||
session = action.sessionData.session,
|
||||
sessionId = action.id,
|
||||
cardId = action.sessionData.wallet.cardId,
|
||||
dAppName = action.sessionData.peerMeta.name
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.BinanceTransaction.Transfer -> {
|
||||
val messageData = BnbHelper.createMessageData(action.order)
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.BnbTransactionDialog(
|
||||
data = messageData,
|
||||
session = action.sessionData.session,
|
||||
sessionId = action.id,
|
||||
cardId = action.sessionData.wallet.cardId,
|
||||
dAppName = action.sessionData.peerMeta.name
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.BinanceTransaction.Sign -> {
|
||||
walletConnectManager.signBnb(
|
||||
action.id, action.data, action.sessionData
|
||||
)
|
||||
}
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleScanCard(wcUri: String) {
|
||||
store.dispatch(GlobalAction.ScanCard(true, { scanResponse ->
|
||||
val card = scanResponse.card
|
||||
if (!card.isMultiwalletAllowed) {
|
||||
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
|
||||
return@ScanCard
|
||||
private fun scanCard(session: WalletConnectSession, chainId: Int?) {
|
||||
val blockchain = WalletConnectNetworkUtils.parseBlockchain(
|
||||
chainId = chainId, peer = session.peerMeta
|
||||
) ?: Blockchain.Ethereum
|
||||
|
||||
store.dispatch(GlobalAction.ScanCard(additionalBlockchainsToDerive = listOf(blockchain),
|
||||
onSuccess = { scanResponse ->
|
||||
handleScanResponse(scanResponse, session, blockchain)
|
||||
},
|
||||
onFailure = {
|
||||
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null))
|
||||
}, R.string.wallet_connect_scan_card_message
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleScanResponse(
|
||||
scanResponse: ScanResponse, session: WalletConnectSession, blockchain: Blockchain
|
||||
) {
|
||||
val card = scanResponse.card
|
||||
if (!card.isMultiwalletAllowed) {
|
||||
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
|
||||
return
|
||||
}
|
||||
|
||||
val walletManager = getWalletManager(scanResponse, blockchain).guard {
|
||||
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null))
|
||||
return
|
||||
}
|
||||
|
||||
val walletManager = getWalletManager(scanResponse).guard {
|
||||
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
|
||||
return@ScanCard
|
||||
}
|
||||
|
||||
val wallet = walletManager.wallet
|
||||
val derivedKey = if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
|
||||
val wallet = walletManager.wallet
|
||||
val derivedKey =
|
||||
if (wallet.publicKey.blockchainKey.contentEquals(wallet.publicKey.seedKey)) {
|
||||
null
|
||||
} else {
|
||||
walletManager.wallet.publicKey.blockchainKey
|
||||
}
|
||||
store.dispatchOnMain(WalletConnectAction.OpenSession(
|
||||
wcUri = wcUri,
|
||||
wallet = WalletForSession(
|
||||
card.cardId,
|
||||
wallet.publicKey.seedKey,
|
||||
derivedKey,
|
||||
wallet.publicKey.derivationPath,
|
||||
card.isTestCard
|
||||
),
|
||||
))
|
||||
}, {
|
||||
store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null))
|
||||
}, R.string.wallet_connect_scan_card_message))
|
||||
val walletForSession = WalletForSession(
|
||||
cardId = scanResponse.card.cardId,
|
||||
walletPublicKey = wallet.publicKey.seedKey,
|
||||
derivedPublicKey = derivedKey,
|
||||
derivationPath = wallet.publicKey.derivationPath,
|
||||
blockchain = wallet.blockchain
|
||||
)
|
||||
val updatedSession = session.copy(wallet = walletForSession)
|
||||
walletConnectManager.updateSession(updatedSession)
|
||||
|
||||
store.dispatchOnMain(WalletConnectAction.AddScanResponse(scanResponse))
|
||||
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.ApproveWcSession(
|
||||
updatedSession
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getWalletManager(scanResponse: ScanResponse): WalletManager? {
|
||||
private fun getWalletManager(
|
||||
scanResponse: ScanResponse, blockchain: Blockchain
|
||||
): WalletManager? {
|
||||
val card = scanResponse.card
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
|
||||
val wcBlockchain = if (scanResponse.card.isTestCard) {
|
||||
val blockchainToMake = if (blockchain == Blockchain.Ethereum && card.isTestCard) {
|
||||
Blockchain.EthereumTestnet
|
||||
} else {
|
||||
Blockchain.Ethereum
|
||||
blockchain
|
||||
}
|
||||
|
||||
return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) {
|
||||
store.state.walletState.getWalletManager(wcBlockchain)
|
||||
?: factory.makeWalletManagerForApp(scanResponse, wcBlockchain)
|
||||
store.state.walletState.getWalletManager(blockchainToMake)
|
||||
?: factory.makeWalletManagerForApp(scanResponse, blockchainToMake)
|
||||
?.also { walletManager ->
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain))
|
||||
}
|
||||
} else {
|
||||
val walletManager = factory.makeWalletManagerForApp(scanResponse, blockchainToMake)
|
||||
if (currenciesRepository.loadCardCurrencies(card.cardId)?.blockchains?.contains(
|
||||
wcBlockchain) == true
|
||||
blockchainToMake
|
||||
) != true
|
||||
) {
|
||||
factory.makeWalletManagerForApp(scanResponse, wcBlockchain)
|
||||
} else {
|
||||
factory.makeWalletManagerForApp(scanResponse, wcBlockchain)
|
||||
?.also {
|
||||
currenciesRepository.saveAddedBlockchain(card.cardId, wcBlockchain)
|
||||
}
|
||||
walletManager?.let {
|
||||
currenciesRepository.saveAddedBlockchain(
|
||||
card.cardId,
|
||||
blockchainToMake
|
||||
)
|
||||
}
|
||||
}
|
||||
return walletManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,12 @@ class WalletConnectReducer {
|
|||
sessions = state.sessions + action.session
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.OpenSession -> {
|
||||
state.copy(loading = true)
|
||||
}
|
||||
is WalletConnectAction.AddScanResponse -> {
|
||||
state.copy(scanResponse = action.scanResponse)
|
||||
}
|
||||
is WalletConnectAction.SetSessionsRestored ->
|
||||
WalletConnectState(sessions = action.sessions)
|
||||
|
||||
|
|
@ -24,8 +30,6 @@ class WalletConnectReducer {
|
|||
state.sessions.filterNot { it.session.toUri() == action.session.toUri() }
|
||||
state.copy(sessions = sessions)
|
||||
}
|
||||
is WalletConnectAction.ScanCard -> state.copy(loading = true)
|
||||
|
||||
is WalletConnectAction.UnsupportedCard -> state.copy(loading = false)
|
||||
is WalletConnectAction.RefuseOpeningSession -> state.copy(loading = false)
|
||||
is WalletConnectAction.OpeningSessionTimeout -> state.copy(loading = false)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
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
|
||||
|
|
@ -13,6 +15,7 @@ import com.trustwallet.walletconnect.models.session.WCSession
|
|||
data class WalletConnectState(
|
||||
val loading: Boolean = false,
|
||||
val sessions: List<WalletConnectSession> = listOf(),
|
||||
val scanResponse: ScanResponse? = null
|
||||
)
|
||||
|
||||
data class WalletConnectSession(
|
||||
|
|
@ -21,18 +24,58 @@ data class WalletConnectSession(
|
|||
val wallet: WalletForSession,
|
||||
val session: WCSession,
|
||||
val peerMeta: WCPeerMeta,
|
||||
)
|
||||
) {
|
||||
fun getAddress(): String? {
|
||||
val key = wallet.derivedPublicKey ?: wallet.walletPublicKey ?: return null
|
||||
return wallet.blockchain?.makeAddresses(key)?.first()?.value
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class WalletForSession(
|
||||
val cardId: String,
|
||||
val walletPublicKey: ByteArray,
|
||||
val derivedPublicKey: ByteArray?,
|
||||
val derivationPath: DerivationPath?,
|
||||
val walletPublicKey: ByteArray? = null,
|
||||
val derivedPublicKey: ByteArray? = null,
|
||||
val derivationPath: DerivationPath? = null,
|
||||
val isTestNet: Boolean = false,
|
||||
val blockchain: Blockchain? = if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum
|
||||
) {
|
||||
val chainId
|
||||
get() = if (isTestNet) 4 else 1
|
||||
|
||||
fun getBlockchainForSession(): Blockchain {
|
||||
return blockchain ?: if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as WalletForSession
|
||||
|
||||
if (cardId != other.cardId) return false
|
||||
if (walletPublicKey != null) {
|
||||
if (other.walletPublicKey == null) return false
|
||||
if (!walletPublicKey.contentEquals(other.walletPublicKey)) return false
|
||||
} else if (other.walletPublicKey != null) return false
|
||||
if (derivedPublicKey != null) {
|
||||
if (other.derivedPublicKey == null) return false
|
||||
if (!derivedPublicKey.contentEquals(other.derivedPublicKey)) return false
|
||||
} else if (other.derivedPublicKey != null) return false
|
||||
if (derivationPath != other.derivationPath) return false
|
||||
if (isTestNet != other.isTestNet) return false
|
||||
if (blockchain != other.blockchain) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = cardId.hashCode()
|
||||
result = 31 * result + (walletPublicKey?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (derivedPublicKey?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (derivationPath?.hashCode() ?: 0)
|
||||
result = 31 * result + isTestNet.hashCode()
|
||||
result = 31 * result + (blockchain?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -46,6 +89,12 @@ sealed class WalletConnectDialog : StateDialog {
|
|||
WalletConnectDialog()
|
||||
|
||||
data class PersonalSign(val data: PersonalSignDialogData) : WalletConnectDialog()
|
||||
data class BnbTransactionDialog(val data: BinanceMessageData,
|
||||
val session: WCSession,
|
||||
val sessionId: Long,
|
||||
val cardId: String,
|
||||
val dAppName: String
|
||||
) : WalletConnectDialog()
|
||||
}
|
||||
|
||||
data class WcTransactionData(
|
||||
|
|
@ -59,7 +108,7 @@ data class WcTransactionData(
|
|||
|
||||
enum class WcTransactionType {
|
||||
EthSignTransaction,
|
||||
EthSendTransaction,
|
||||
EthSendTransaction
|
||||
}
|
||||
|
||||
data class WcPersonalSignData(
|
||||
|
|
@ -68,4 +117,29 @@ data class WcPersonalSignData(
|
|||
val id: Long,
|
||||
val dialogData: PersonalSignDialogData,
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
sealed class BinanceMessageData(
|
||||
val address: String,
|
||||
val data: ByteArray,
|
||||
) {
|
||||
class Trade(
|
||||
val tradeData: List<TradeData>,
|
||||
address: String,
|
||||
data: ByteArray,
|
||||
) : BinanceMessageData(address, data)
|
||||
|
||||
class Transfer(
|
||||
val outputAddress: String,
|
||||
val amount: String,
|
||||
address: String,
|
||||
data: ByteArray,
|
||||
) : BinanceMessageData(address, data)
|
||||
}
|
||||
|
||||
data class TradeData(
|
||||
val price: String,
|
||||
val quantity: String,
|
||||
val amount: String,
|
||||
val symbol: String
|
||||
)
|
||||
|
|
@ -73,7 +73,10 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
}
|
||||
tv_card_id.text = cardId
|
||||
tv_issuer.text = state.cardInfo.issuer
|
||||
tv_signed_hashes.text = state.cardInfo.signedHashes.toString()
|
||||
tv_signed_hashes.text = getString(
|
||||
R.string.details_row_subtitle_signed_hashes_format,
|
||||
state.cardInfo.signedHashes.toString()
|
||||
)
|
||||
}
|
||||
|
||||
tv_signed_hashes.show(!state.isTangemTwins)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class QrScanFragment : Fragment(0), ZXingScannerView.ResultHandler {
|
|||
store.dispatch(NavigationAction.PopBackTo())
|
||||
|
||||
if (!result.text.isNullOrBlank()) {
|
||||
store.dispatch(WalletConnectAction.ScanCard(result.text))
|
||||
store.dispatch(WalletConnectAction.OpenSession(result.text))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
|
||||
class BnbTransactionDialog {
|
||||
companion object {
|
||||
fun create(
|
||||
data: BinanceMessageData,
|
||||
session: WCSession,
|
||||
sessionId: Long,
|
||||
cardId: String,
|
||||
dAppName: String,
|
||||
context: Context,
|
||||
): AlertDialog {
|
||||
|
||||
val message = when (data) {
|
||||
is BinanceMessageData.Trade -> data.tradeData.map {
|
||||
context.getString(
|
||||
R.string.wallet_connect_bnb_trade_order_message,
|
||||
it.symbol,
|
||||
it.price,
|
||||
it.quantity,
|
||||
it.amount
|
||||
)
|
||||
}.joinToString ( "\n\n" )
|
||||
is BinanceMessageData.Transfer -> context.getString(
|
||||
R.string.wallet_connect_bnb_transaction_message,
|
||||
data.address,
|
||||
data.outputAddress,
|
||||
data.amount
|
||||
)
|
||||
}
|
||||
|
||||
val fullMessage = context.getString(
|
||||
R.string.wallet_connect_bnb_sign_message,
|
||||
dAppName, cardId, message
|
||||
)
|
||||
val positiveButtonTitle = context.getText(R.string.common_sign)
|
||||
|
||||
return AlertDialog.Builder(context).apply {
|
||||
setTitle(context.getString(R.string.wallet_connect))
|
||||
setMessage(fullMessage)
|
||||
setPositiveButton(positiveButtonTitle) { _, _ ->
|
||||
store.dispatch(WalletConnectAction.BinanceTransaction.Sign(
|
||||
id = sessionId,
|
||||
data = data.data,
|
||||
sessionData = session
|
||||
))
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.common_reject)) { _, _ ->
|
||||
store.dispatch(WalletConnectAction.RejectRequest(session, sessionId))
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ class ClipboardOrScanQrDialog {
|
|||
setTitle(context.getString(R.string.wallet_connect))
|
||||
setMessage(context.getText(R.string.wallet_connect_clipboard_alert))
|
||||
setPositiveButton(context.getText(R.string.wallet_connect_paste_from_clipboard)) { _, _ ->
|
||||
store.dispatch(WalletConnectAction.ScanCard(wcUri))
|
||||
store.dispatch(WalletConnectAction.OpenSession(wcUri))
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.wallet_connect_scan_new_code)) { _, _ ->
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.QrScan))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package com.tangem.tap.features.home.redux
|
||||
|
||||
import com.tangem.tap.common.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.GetCardSourceParams
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
|
|
@ -46,7 +49,13 @@ private val homeMiddleware: Middleware<AppState> = { dispatch, state ->
|
|||
}
|
||||
}
|
||||
is HomeAction.ReadCard -> handleReadCard()
|
||||
is HomeAction.GoToShop -> store.dispatchOpenUrl(HomeMiddleware.CARD_SHOP_URI)
|
||||
is HomeAction.GoToShop -> {
|
||||
store.dispatchOpenUrl(HomeMiddleware.CARD_SHOP_URI)
|
||||
store.state.globalState.analyticsHandlers?.triggerEvent(
|
||||
event = AnalyticsEvent.GET_CARD,
|
||||
params = mapOf(AnalyticsParam.SOURCE.param to GetCardSourceParams.WELCOME.param)
|
||||
)
|
||||
}
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
|
|
@ -58,7 +67,7 @@ private fun handleReadCard() {
|
|||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
|
||||
} else {
|
||||
changeButtonState(ButtonState.PROGRESS)
|
||||
store.dispatch(GlobalAction.ScanCard(false, { scanResponse ->
|
||||
store.dispatch(GlobalAction.ScanCard( onSuccess = { scanResponse ->
|
||||
store.state.globalState.tapWalletManager.updateConfigManager(scanResponse)
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
|
||||
|
|
@ -79,7 +88,7 @@ private fun handleReadCard() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
}, onFailure = {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.onboarding
|
||||
|
||||
import android.content.Context
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tangem.tap.common.extensions.copyToClipboard
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
|
|
@ -23,6 +24,7 @@ class AddressInfoBottomSheetDialog(
|
|||
|
||||
init {
|
||||
this.setContentView(R.layout.dialog_onboarding_address_info)
|
||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
setOnCancelListener { store.dispatchDialogHide() }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,25 +37,6 @@ class OnboardingManager(
|
|||
return scanResponse.card.getOrLoadCardArtworkUrl(cardInfo)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
val balance = when (val result = walletManager.safeUpdate()) {
|
||||
is Result.Success -> {
|
||||
|
|
|
|||
|
|
@ -130,7 +130,11 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
}
|
||||
}
|
||||
is TwinCardsAction.Wallet.LaunchFirstStep -> {
|
||||
val manager = TwinCardsManager(getScanResponse().card, action.reader)
|
||||
val manager = TwinCardsManager(
|
||||
card = getScanResponse().card,
|
||||
assetReader = action.reader,
|
||||
analyticsHandler = store.state.globalState.analyticsHandlers
|
||||
)
|
||||
store.dispatch(TwinCardsAction.CardsManager.Set(manager))
|
||||
|
||||
scope.launch {
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import com.squareup.picasso.Picasso
|
|||
import com.tangem.Message
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.common.redux.navigation.ShareElement
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.features.onboarding.products.wallet.redux
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class OnboardingWalletAction : Action {
|
||||
|
|
@ -14,7 +13,7 @@ sealed class OnboardingWalletAction : Action {
|
|||
object ProceedBackup : OnboardingWalletAction()
|
||||
|
||||
object LoadArtwork : OnboardingWalletAction()
|
||||
class SetArtworkUrl(val artwork: Artwork) : OnboardingWalletAction()
|
||||
class SetArtworkUrl(val artworkUrl: String?) : OnboardingWalletAction()
|
||||
|
||||
object OnBackPressed : OnboardingWalletAction()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.card.Card
|
||||
import com.tangem.operations.backup.BackupService
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.GetCardSourceParams
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.withMainContext
|
||||
|
|
@ -14,8 +17,8 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.extensions.hasWallets
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware.Companion.BUY_WALLET_URL
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
|
|
@ -61,11 +64,16 @@ private fun handleWalletAction(action: Action) {
|
|||
}
|
||||
store.dispatch(action)
|
||||
}
|
||||
is OnboardingNoteAction.LoadCardArtwork -> {
|
||||
// scope.launch {
|
||||
// val artwork = onboardingManager.loadArtwork()
|
||||
// store.dispatchOnMain(OnboardingWalletAction.SetArtworkUrl(artwork))
|
||||
// }
|
||||
is OnboardingWalletAction.LoadArtwork -> {
|
||||
scope.launch {
|
||||
val artwork = onboardingManager?.loadArtworkUrl()
|
||||
val cardArtwork = if (artwork == Artwork.DEFAULT_IMG_URL) {
|
||||
null
|
||||
} else {
|
||||
artwork
|
||||
}
|
||||
store.dispatchOnMain(OnboardingWalletAction.SetArtworkUrl(cardArtwork))
|
||||
}
|
||||
}
|
||||
is OnboardingWalletAction.CreateWallet -> {
|
||||
scope.launch {
|
||||
|
|
@ -207,7 +215,13 @@ private fun handleBackupAction(action: BackupAction) {
|
|||
}
|
||||
}
|
||||
is BackupAction.GoToShop -> {
|
||||
store.dispatchOpenUrl(BUY_WALLET_URL)
|
||||
backupState.buyAdditionalCardsUrl?.let { url ->
|
||||
store.dispatchOpenUrl(url)
|
||||
store.state.globalState.analyticsHandlers?.triggerEvent(
|
||||
event = AnalyticsEvent.GET_CARD,
|
||||
params = mapOf(AnalyticsParam.SOURCE.param to GetCardSourceParams.ONBOARDING.param)
|
||||
)
|
||||
}
|
||||
}
|
||||
is BackupAction.FinishAddingBackupCards -> {
|
||||
if (backupService.addedBackupCardsCount == 1) {
|
||||
|
|
@ -223,11 +237,13 @@ private fun handleBackupAction(action: BackupAction) {
|
|||
AccessCodeError.CodeTooShort
|
||||
))
|
||||
} else {
|
||||
store.dispatch(BackupAction.SetAccessCodeError(null))
|
||||
store.dispatch(BackupAction.SaveFirstAccessCode(action.accessCode))
|
||||
}
|
||||
}
|
||||
is BackupAction.SaveAccessCodeConfirmation -> {
|
||||
if (action.accessCodeConfirmation == backupState.accessCode) {
|
||||
store.dispatch(BackupAction.SetAccessCodeError(null))
|
||||
backupService.setAccessCode(action.accessCodeConfirmation)
|
||||
store.dispatch(BackupAction.PrepareToWritePrimaryCard)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingWallet
|
|||
OnboardingWalletState(step = OnboardingWalletStep.CreateWallet)
|
||||
is OnboardingWalletAction.ProceedBackup -> state.copy(step = OnboardingWalletStep.Backup)
|
||||
is OnboardingWalletAction.SetArtworkUrl -> {
|
||||
state.copy(artwork = action.artwork)
|
||||
state.copy(cardArtworkUrl = action.artworkUrl)
|
||||
}
|
||||
is OnboardingWalletAction.Done -> state.copy(step = OnboardingWalletStep.Done)
|
||||
else -> state
|
||||
|
|
@ -60,7 +60,7 @@ class BackupReducer {
|
|||
state.copy(backupStep = BackupStep.SetAccessCode)
|
||||
}
|
||||
BackupAction.ShowEnterAccessCodeScreen -> {
|
||||
state.copy(backupStep = BackupStep.EnterAccessCode)
|
||||
state.copy(backupStep = BackupStep.EnterAccessCode, accessCodeError = null)
|
||||
}
|
||||
is BackupAction.SaveFirstAccessCode -> {
|
||||
state.copy(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.features.onboarding.products.wallet.redux
|
|||
|
||||
import android.graphics.Bitmap
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import org.rekotlin.StateType
|
||||
|
||||
/**
|
||||
|
|
@ -11,7 +10,7 @@ import org.rekotlin.StateType
|
|||
data class OnboardingWalletState(
|
||||
val step: OnboardingWalletStep = OnboardingWalletStep.None,
|
||||
val backupState: BackupState = BackupState(),
|
||||
val artwork: Artwork? = null,
|
||||
val cardArtworkUrl: String? = null,
|
||||
val showConfetti: Boolean = false,
|
||||
) : StateType {
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import android.animation.PropertyValuesHolder
|
|||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
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
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapView
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapViewState
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
|
||||
|
||||
class BackupCardsWidget(
|
||||
val leapfrogWidget: LeapfrogWidget,
|
||||
|
|
|
|||
|
|
@ -2,23 +2,22 @@ package com.tangem.tap.features.onboarding.products.wallet.ui
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import androidx.transition.TransitionManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.common.CardIdFormatter
|
||||
import com.tangem.common.core.CardIdDisplayFormat
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget
|
||||
import com.tangem.tap.common.postUi
|
||||
import com.tangem.tap.features.FragmentOnBackPressedHandler
|
||||
import com.tangem.tap.features.addBackPressHandler
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.*
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware.Companion.BUY_WALLET_URL
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.AccessCodeDialog
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -62,6 +61,8 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
|
|||
store.dispatch(OnboardingWalletAction.Init)
|
||||
toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
|
||||
|
||||
store.dispatch(OnboardingWalletAction.LoadArtwork)
|
||||
|
||||
addBackPressHandler(this)
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +85,10 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
|
|||
|
||||
requireActivity().invalidateOptionsMenu()
|
||||
|
||||
state.artwork?.artwork?.let { imv_front_card.setImageBitmap(it) }
|
||||
loadImageIntoImageView(state.cardArtworkUrl, imv_front_card)
|
||||
loadImageIntoImageView(state.cardArtworkUrl, imv_first_backup_card)
|
||||
loadImageIntoImageView(state.cardArtworkUrl, imv_second_backup_card)
|
||||
|
||||
pb_state.max = 6
|
||||
pb_state.progress = state.getProgressStep()
|
||||
|
||||
|
|
@ -99,6 +103,14 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadImageIntoImageView(url: String?, view: ImageView) {
|
||||
Picasso.get()
|
||||
.load(url)
|
||||
.error(R.drawable.card_placeholder_black)
|
||||
.placeholder(R.drawable.card_placeholder_black)
|
||||
?.into(view)
|
||||
}
|
||||
|
||||
private fun setupCreateWalletState() {
|
||||
btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet)
|
||||
btn_main_action.setOnClickListener { store.dispatch(OnboardingWalletAction.CreateWallet) }
|
||||
|
|
@ -124,7 +136,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
|
|||
BackupStep.ReenterAccessCode -> showReenterAccessCode(state)
|
||||
is BackupStep.WritePrimaryCard -> showWritePrimaryCard(state)
|
||||
is BackupStep.WriteBackupCard -> showWriteBackupCard(state)
|
||||
BackupStep.Finished -> showSuccess(state)
|
||||
BackupStep.Finished -> showSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -182,11 +194,10 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
|
|||
|
||||
layout_buttons_add_cards.show()
|
||||
layout_buttons_common.hide()
|
||||
btn_add_card.text = getText(R.string.onboarding_button_add_backup_card)
|
||||
if (state.backupCardsNumber < state.maxBackupCards) {
|
||||
btn_add_card.text = getText(R.string.onboarding_button_add_backup_card)
|
||||
btn_add_card.setOnClickListener { store.dispatch(BackupAction.AddBackupCard) }
|
||||
} else {
|
||||
btn_add_card.text = "You hit the maximum"
|
||||
btn_add_card.isEnabled = false
|
||||
}
|
||||
|
||||
|
|
@ -240,15 +251,11 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
|
|||
private fun showEnterAccessCode(state: BackupState) {
|
||||
accessCodeDialog?.showEnterAccessCode()
|
||||
accessCodeDialog?.showError(state.accessCodeError)
|
||||
postUi(2000) { store.dispatch(BackupAction.SetAccessCodeError(null)) }
|
||||
|
||||
}
|
||||
|
||||
private fun showReenterAccessCode(state: BackupState) {
|
||||
accessCodeDialog?.showReenterAccessCode()
|
||||
accessCodeDialog?.showError(state.accessCodeError)
|
||||
postUi(2000) { store.dispatch(BackupAction.SetAccessCodeError(null)) }
|
||||
|
||||
}
|
||||
|
||||
private fun showWritePrimaryCard(state: BackupState) {
|
||||
|
|
@ -312,7 +319,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
|
|||
tv_header.text =
|
||||
getString(R.string.onboarding_title_backup_card_format, cardNumber)
|
||||
tv_body.text = getString(
|
||||
R.string.onboarding_subtitle_scan_primary_card_format,
|
||||
R.string.onboarding_subtitle_scan_backup_card_format,
|
||||
cardIdFormatter.getFormattedCardId(state.backupCardIds[cardNumber - 1])
|
||||
)
|
||||
btn_main_action.text = getString(
|
||||
|
|
@ -323,21 +330,16 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
|
|||
btn_main_action.setOnClickListener { store.dispatch(BackupAction.WriteBackupCard(cardNumber)) }
|
||||
}
|
||||
|
||||
private fun showSuccess(state: BackupState) {
|
||||
private fun showSuccess() {
|
||||
tv_header.text = getText(R.string.onboarding_done_header)
|
||||
val text = when (state.backupCardsNumber) {
|
||||
1 -> getText(R.string.onboarding_subtitle_success_backup_one_card)
|
||||
2 -> getText(R.string.onboarding_subtitle_success_backup)
|
||||
else -> getText(R.string.onboarding_subtitle_success_tangem_wallet_onboarding)
|
||||
}
|
||||
|
||||
tv_header.show()
|
||||
tv_body.show()
|
||||
view_pager_backup_info.hide()
|
||||
tab_layout_backup_info.hide()
|
||||
|
||||
tv_body.text = text
|
||||
btn_main_action.text = getText(R.string.common_continue)
|
||||
tv_body.text = getText(R.string.onboarding_subtitle_success_tangem_wallet_onboarding)
|
||||
btn_main_action.text = getText(R.string.onboarding_button_continue_wallet)
|
||||
btn_alternative_action.hide()
|
||||
btn_main_action.setOnClickListener {
|
||||
lav_confetti.cancelAnimation()
|
||||
|
|
@ -362,7 +364,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
|
|||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.shop_menu -> {
|
||||
store.dispatchOpenUrl(BUY_WALLET_URL)
|
||||
store.dispatch(BackupAction.GoToShop)
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ import com.tangem.blockchain.extensions.SimpleResult
|
|||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.AnalyticsParam
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -180,7 +181,7 @@ private fun sendTransaction(
|
|||
tangemSdk.config.linkedTerminal = isLinkedTerminal
|
||||
when (sendResult) {
|
||||
is SimpleResult.Success -> {
|
||||
FirebaseAnalyticsHandler.triggerEvent(
|
||||
store.state.globalState.analyticsHandlers?.triggerEvent(
|
||||
event = AnalyticsEvent.TRANSACTION_IS_SENT,
|
||||
card = card,
|
||||
blockchain = walletManager.wallet.blockchain.currency
|
||||
|
|
@ -204,13 +205,13 @@ private fun sendTransaction(
|
|||
}
|
||||
is SimpleResult.Failure -> {
|
||||
when (sendResult.error) {
|
||||
is CreateAccountUnderfunded -> {
|
||||
val error = sendResult.error as CreateAccountUnderfunded
|
||||
is BlockchainSdkError.CreateAccountUnderfunded -> {
|
||||
val error = sendResult.error as BlockchainSdkError.CreateAccountUnderfunded
|
||||
val reserve = error.minReserve.value?.stripZeroPlainString() ?: "0"
|
||||
val symbol = error.minReserve.currencySymbol
|
||||
dispatch(SendAction.SendError(TapError.CreateAccountUnderfunded(listOf(reserve, symbol))))
|
||||
}
|
||||
is SendException -> {
|
||||
is BlockchainSdkError.SendException -> {
|
||||
sendResult.error?.let { FirebaseCrashlytics.getInstance().recordException(it) }
|
||||
}
|
||||
is Throwable -> {
|
||||
|
|
@ -239,11 +240,11 @@ private fun sendTransaction(
|
|||
}
|
||||
else -> {
|
||||
(sendResult.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.SendTransaction,
|
||||
Analytics.ActionToLog.SendTransaction,
|
||||
mapOf(
|
||||
FirebaseAnalyticsHandler.AnalyticsParam.BLOCKCHAIN
|
||||
AnalyticsParam.BLOCKCHAIN
|
||||
to walletManager.wallet.blockchain.currency),
|
||||
card = card,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ 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.*
|
||||
import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget
|
||||
import com.tangem.tap.common.toggleWidget.ViewStateWidget
|
||||
import com.tangem.tap.features.BaseStoreFragment
|
||||
import com.tangem.tap.features.addBackPressHandler
|
||||
import com.tangem.tap.features.send.redux.*
|
||||
|
|
@ -40,8 +41,6 @@ import com.tangem.tap.mainScope
|
|||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fragment_send.*
|
||||
import kotlinx.android.synthetic.main.fragment_send.rv_warning_messages
|
||||
import kotlinx.android.synthetic.main.fragment_wallet.*
|
||||
import kotlinx.android.synthetic.main.layout_send_address_payid.*
|
||||
import kotlinx.android.synthetic.main.layout_send_amount.*
|
||||
import kotlinx.android.synthetic.main.layout_send_fee.*
|
||||
|
|
@ -298,7 +297,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
|
||||
@ExperimentalCoroutinesApi
|
||||
fun EditText.inputtedTextAsFlow(): Flow<String> = callbackFlow {
|
||||
val watcher = addTextChangedListener { editable -> offer(editable?.toString() ?: "") }
|
||||
val watcher = addTextChangedListener { editable -> trySend(editable?.toString() ?: "") }
|
||||
awaitClose { removeTextChangedListener(watcher) }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -137,6 +137,11 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
|
|||
|
||||
adapter.submitList(state.sendWarningsList)
|
||||
rv.show(state.sendWarningsList.isNotEmpty())
|
||||
|
||||
fg.toolbar.title = fg.getString(
|
||||
R.string.send_title_currency_format,
|
||||
state.amountState.mainCurrency.currencySymbol
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleAddressPayIdState(fg: BaseStoreFragment, state: AddressPayIdState) {
|
||||
|
|
|
|||
|
|
@ -23,29 +23,36 @@ sealed class CurrencyListItem {
|
|||
blockchains: List<Blockchain>,
|
||||
tokens: List<Token>,
|
||||
): List<CurrencyListItem> {
|
||||
val blockchainsTitle = R.string.add_tokens_subtitle_blockchains
|
||||
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
|
||||
return createBlockchainList(blockchains) +
|
||||
createTokensList(R.string.add_tokens_subtitle_ethereum_tokens, Blockchain.Ethereum, tokens) +
|
||||
createTokensList(R.string.add_tokens_subtitle_bsc_tokens, Blockchain.BSC, tokens) +
|
||||
createTokensList(R.string.add_tokens_subtitle_binance_tokens, Blockchain.Binance, tokens) +
|
||||
createTokensList(R.string.add_tokens_subtitle_polygon_tokens, Blockchain.Polygon, tokens) +
|
||||
createTokensList(R.string.add_tokens_subtitle_avalanche_tokens, Blockchain.Avalanche, 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(ethereumTokensTitle, blockchain = Blockchain.Ethereum)) +
|
||||
ethereumTokens.map { TokenListItem(it) } +
|
||||
listOf(TitleListItem(bscTokensTitle, blockchain = Blockchain.BSC)) +
|
||||
bscTokens.map { TokenListItem(it) } +
|
||||
listOf(TitleListItem(binanceTokensTitle, blockchain = Blockchain.Binance)) +
|
||||
binanceTokens.map { TokenListItem(it) }
|
||||
private fun createBlockchainList(blockchains: List<Blockchain>): List<CurrencyListItem> {
|
||||
val blockchainsTitle = R.string.add_tokens_subtitle_blockchains
|
||||
return listOf(TitleListItem(blockchainsTitle)) + blockchains.map { BlockchainListItem(it) }
|
||||
}
|
||||
|
||||
private fun createTokensList(
|
||||
@StringRes titleResId: Int,
|
||||
blockchain: Blockchain,
|
||||
tokens: List<Token>
|
||||
): List<CurrencyListItem> {
|
||||
val filteredTokens = tokens.filter {
|
||||
it.blockchain == blockchain || it.blockchain == blockchain.getTestnetVersion()
|
||||
}
|
||||
val tokensListItem = filteredTokens.map { TokenListItem(it) }
|
||||
return listOf(TitleListItem(titleResId, blockchain = blockchain)) + tokensListItem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun List<CurrencyListItem>.removeTokensForBlockchain(blockchain: Blockchain): List<CurrencyListItem> {
|
||||
return filterNot {
|
||||
it is CurrencyListItem.TokenListItem && it.token.blockchain == blockchain
|
||||
it is CurrencyListItem.TokenListItem && it.isTheSameOrTestnetBlockchain(blockchain)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -53,18 +60,31 @@ fun List<CurrencyListItem>.addTokensForBlockchain(
|
|||
blockchain: Blockchain, fullCurrenciesList: List<CurrencyListItem>,
|
||||
): List<CurrencyListItem> {
|
||||
val tokensToAdd = fullCurrenciesList.filter {
|
||||
it is CurrencyListItem.TokenListItem && it.token.blockchain == blockchain
|
||||
it is CurrencyListItem.TokenListItem && it.isTheSameOrTestnetBlockchain(blockchain)
|
||||
}
|
||||
val indexToInsert = indexOfFirst {
|
||||
it is CurrencyListItem.TitleListItem && it.blockchain == blockchain
|
||||
it is CurrencyListItem.TitleListItem && it.isTheSameOrTestnetBlockchain(blockchain)
|
||||
} + 1
|
||||
return this.toMutableList().apply { addAll(indexToInsert, tokensToAdd) }
|
||||
}
|
||||
|
||||
fun List<CurrencyListItem>.toggleHeaderContentShownValue(blockchain: Blockchain) {
|
||||
map {
|
||||
if (it is CurrencyListItem.TitleListItem && it.blockchain == blockchain) {
|
||||
if (it is CurrencyListItem.TitleListItem && it.isTheSameOrTestnetBlockchain(blockchain)) {
|
||||
it.isContentShown = !it.isContentShown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if you do not check the testNet blockchains, it will not collapse
|
||||
private fun CurrencyListItem.isTheSameOrTestnetBlockchain(blockchain: Blockchain): Boolean {
|
||||
return when (this) {
|
||||
is CurrencyListItem.TitleListItem -> this.blockchain?.isTheSameOrTestnetBlockchain(blockchain) ?: false
|
||||
is CurrencyListItem.BlockchainListItem -> this.blockchain.isTheSameOrTestnetBlockchain(blockchain)
|
||||
is CurrencyListItem.TokenListItem -> this.token.blockchain.isTheSameOrTestnetBlockchain(blockchain)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Blockchain.isTheSameOrTestnetBlockchain(blockchain: Blockchain): Boolean {
|
||||
return this == blockchain || this == blockchain.getTestnetVersion()
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ sealed class TokensAction : Action {
|
|||
data class SetAddedCurrencies(val wallets: List<WalletData>) : TokensAction()
|
||||
|
||||
data class ToggleShowTokensForBlockchain(val isShown: Boolean, val blockchain: Blockchain) : TokensAction()
|
||||
object OpenAllTokens : TokensAction()
|
||||
|
||||
data class SaveChanges(val addedItems: List<CurrencyListItem>) : TokensAction()
|
||||
}
|
||||
|
|
@ -4,11 +4,11 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.common.hdWallet.ExtendedPublicKey
|
||||
import com.tangem.tap.common.extensions.ByteArrayKey
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.toMapKey
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
|
|
@ -32,7 +32,7 @@ class TokensMiddleware {
|
|||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is TokensAction.LoadCurrencies -> handleLoadCurrencies(action)
|
||||
is TokensAction.LoadCurrencies -> handleLoadCurrencies()
|
||||
is TokensAction.SaveChanges -> handleSaveChanges(action)
|
||||
}
|
||||
next(action)
|
||||
|
|
@ -40,15 +40,15 @@ class TokensMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleLoadCurrencies(action: TokensAction.LoadCurrencies) {
|
||||
private fun handleLoadCurrencies() {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
val isTestcard = scanResponse.card.isTestCard
|
||||
|
||||
val tokens = currenciesRepository.getPopularTokens(isTestcard)
|
||||
val tokens = currenciesRepository.getSupportedTokens(isTestcard)
|
||||
val blockchains = currenciesRepository.getBlockchains(
|
||||
cardFirmware = scanResponse.card.firmwareVersion,
|
||||
isTestNet = isTestcard
|
||||
)
|
||||
).sortedBy { it.fullName }
|
||||
val currencies =
|
||||
CurrencyListItem.createListOfCurrencies(blockchains, tokens).toMutableList()
|
||||
store.dispatch(TokensAction.LoadCurrencies.Success(currencies))
|
||||
|
|
@ -94,14 +94,14 @@ class TokensMiddleware {
|
|||
is CompletionResult.Success -> {
|
||||
val newDerivedKeys = result.data.entries
|
||||
val updatedDerivedKeys =
|
||||
mutableMapOf<KeyWalletPublicKey, List<ExtendedPublicKey>>()
|
||||
mutableMapOf<KeyWalletPublicKey, ExtendedPublicKeysMap>()
|
||||
|
||||
newDerivedKeys.forEach { entry ->
|
||||
val derivationData = derivationDataList.find {
|
||||
it.mapKeyOfWalletPublicKey == entry.key
|
||||
} ?: return@forEach
|
||||
updatedDerivedKeys[entry.key] =
|
||||
derivationData.alreadyDerivedKeys + entry.value.toList()
|
||||
ExtendedPublicKeysMap(derivationData.alreadyDerivedKeys + entry.value)
|
||||
}
|
||||
|
||||
val updatedScanResponse = scanResponse.copy(
|
||||
|
|
@ -133,9 +133,9 @@ class TokensMiddleware {
|
|||
.mapNotNull { it.derivationPath() }
|
||||
|
||||
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
|
||||
val alreadyDerivedKeys =
|
||||
scanResponse.derivedKeys[mapKeyOfWalletPublicKey]?.toMutableList() ?: mutableListOf()
|
||||
val alreadyDerivedPaths = alreadyDerivedKeys.map { it.derivationPath }
|
||||
val alreadyDerivedKeys: ExtendedPublicKeysMap =
|
||||
scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap())
|
||||
val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList()
|
||||
|
||||
val toDerive = derivationPathsCandidates.filterNot { alreadyDerivedPaths.contains(it) }
|
||||
return DerivationData(
|
||||
|
|
@ -147,7 +147,7 @@ class TokensMiddleware {
|
|||
|
||||
private class DerivationData(
|
||||
val derivations: Pair<ByteArrayKey, List<DerivationPath>>,
|
||||
val alreadyDerivedKeys: List<ExtendedPublicKey>,
|
||||
val alreadyDerivedKeys: ExtendedPublicKeysMap,
|
||||
val mapKeyOfWalletPublicKey: ByteArrayKey
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,11 @@ private fun internalReduce(action: Action, state: AppState): TokensState {
|
|||
tokensState.copy(shownCurrencies = shownCurrencies)
|
||||
}
|
||||
}
|
||||
is TokensAction.OpenAllTokens -> {
|
||||
val shownCurrencies = tokensState.currencies
|
||||
shownCurrencies.forEach { if (it is CurrencyListItem.TitleListItem) it.isContentShown = true}
|
||||
tokensState.copy(shownCurrencies = tokensState.currencies)
|
||||
}
|
||||
else -> tokensState
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ fun SearchView.inputtedTextAsFlow(): Flow<String> = callbackFlow {
|
|||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
offer(newText ?: "")
|
||||
trySend(newText ?: "")
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
val list = mutableListOf<CurrencyListItem>()
|
||||
|
||||
if (!query.isNullOrEmpty()) {
|
||||
store.dispatch(TokensAction.OpenAllTokens)
|
||||
val queryNormalized = query.toString().toLowerCase(Locale.US)
|
||||
list.addAll(
|
||||
unfilteredList.filter { element ->
|
||||
|
|
@ -181,7 +182,10 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
Picasso.get().loadCurrenciesIcon(view.iv_currency, view.tv_token_letter, token, token.blockchain)
|
||||
|
||||
view.tv_currency_name.text = token.name
|
||||
view.tv_currency_symbol.text = token.symbol
|
||||
view.tv_currency_symbol.text = view.context.getString(
|
||||
R.string.token_symbol_address_format,
|
||||
token.symbol, token.contractAddress
|
||||
)
|
||||
view.btn_add_token.setOnClickListener {
|
||||
onItemAddListener.invoke(currency)
|
||||
currency.isAdded = true
|
||||
|
|
@ -214,18 +218,30 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
|
||||
if (title.blockchain != null) {
|
||||
view.cl_subtitle_container.setOnClickListener {
|
||||
val rotation = if (title.isContentShown) -90f else 0f
|
||||
val rotation = if (title.isContentShown) -90f else 90f
|
||||
store.dispatch(TokensAction.ToggleShowTokensForBlockchain(
|
||||
title.isContentShown, title.blockchain
|
||||
))
|
||||
view.iv_toggle_sublist_visibility.animate().rotation(rotation)
|
||||
view.iv_toggle_sublist_visibility.animate().rotation(rotation).withEndAction {
|
||||
showArrow(view, title.isContentShown)
|
||||
}
|
||||
}
|
||||
view.iv_toggle_sublist_visibility.show()
|
||||
view.iv_toggle_sublist_visibility.setImageResource(R.drawable.ic_arrow_angle_down)
|
||||
showArrow(view, title.isContentShown)
|
||||
} else {
|
||||
view.iv_toggle_sublist_visibility.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showArrow(view: View, isContentShown: Boolean) {
|
||||
view.iv_toggle_sublist_visibility.show()
|
||||
val drawable = if (isContentShown) {
|
||||
R.drawable.ic_arrow_angle_down
|
||||
} else {
|
||||
R.drawable.ic_arrow_angle_right
|
||||
}
|
||||
view.iv_toggle_sublist_visibility.setImageResource(drawable)
|
||||
view.iv_toggle_sublist_visibility.rotation = 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -146,4 +146,5 @@ sealed class WalletAction : Action {
|
|||
|
||||
data class ChangeSelectedAddress(val type: AddressType) : WalletAction()
|
||||
|
||||
data class SetWalletRent(val blockchain: Blockchain, val minRent: String, val rentExempt: String): WalletAction()
|
||||
}
|
||||
|
|
@ -246,7 +246,8 @@ data class WalletData(
|
|||
val fiatRateString: String? = null,
|
||||
val fiatRate: BigDecimal? = null,
|
||||
val mainButton: WalletMainButton = WalletMainButton.SendButton(false),
|
||||
val currency: Currency
|
||||
val currency: Currency,
|
||||
val walletRent: WalletRent? = null,
|
||||
) {
|
||||
fun shouldShowMultipleAddress(): Boolean {
|
||||
val listOfAddresses = walletAddresses?.list ?: return false
|
||||
|
|
@ -254,6 +255,11 @@ data class WalletData(
|
|||
}
|
||||
}
|
||||
|
||||
data class WalletRent(
|
||||
val minRentValue: String,
|
||||
val rentExemptValue: String
|
||||
)
|
||||
|
||||
sealed interface Currency {
|
||||
|
||||
val blockchain: com.tangem.blockchain.common.Blockchain
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.tap.features.wallet.redux.middlewares
|
||||
|
||||
import com.tangem.blockchain.blockchains.solana.RentProvider
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.redux.global.GlobalState
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
|
|
@ -31,6 +33,7 @@ class MultiWalletMiddleware {
|
|||
when (action) {
|
||||
is WalletAction.MultiWallet.AddWalletManagers -> {
|
||||
globalState.feedbackManager?.infoHolder?.setWalletsInfo(action.walletManagers)
|
||||
action.walletManagers.forEach { checkForRentWarning(it) }
|
||||
}
|
||||
is WalletAction.MultiWallet.SelectWallet -> {
|
||||
if (action.walletData != null) {
|
||||
|
|
@ -132,7 +135,7 @@ class MultiWalletMiddleware {
|
|||
val walletManager = walletState?.getWalletManager(Blockchain.Ethereum)
|
||||
?: walletFactory.makeWalletManagerForApp(scanResponse, Blockchain.Ethereum)
|
||||
|
||||
val tokenFinder = walletManager as TokenFinder
|
||||
val tokenFinder = walletManager as? TokenFinder ?: return
|
||||
scope.launch {
|
||||
val result = tokenFinder.findTokens()
|
||||
withContext(Dispatchers.Main) {
|
||||
|
|
@ -197,6 +200,26 @@ class MultiWalletMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkForRentWarning(walletManager: WalletManager) {
|
||||
val rentProvider = walletManager as? RentProvider ?: return
|
||||
|
||||
scope.launch {
|
||||
when (val result = rentProvider.minimalBalanceForRentExemption()) {
|
||||
is Result.Success -> {
|
||||
val currency = walletManager.wallet.blockchain.currency
|
||||
val minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency")
|
||||
val rentExempt = ("${result.data.stripZeroPlainString()} $currency")
|
||||
store.dispatchOnMain(WalletAction.SetWalletRent(
|
||||
blockchain = walletManager.wallet.blockchain,
|
||||
minRent = minRent,
|
||||
rentExempt = rentExempt
|
||||
))
|
||||
}
|
||||
is Result.Failure -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTokens(
|
||||
tokens: List<Token>,
|
||||
walletState: WalletState?,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ 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.analytics.Analytics
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -104,13 +104,13 @@ class WalletMiddleware {
|
|||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val scanNoteResponse = globalState.scanResponse?.copy(card = result.data)
|
||||
scanNoteResponse?.let { store.onCardScanned(scanNoteResponse, false) }
|
||||
scanNoteResponse?.let { store.onCardScanned(scanNoteResponse) }
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
|
||||
Analytics.ActionToLog.CreateWallet,
|
||||
card = store.state.detailsState.scanResponse?.card
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import com.tangem.blockchain.extensions.SimpleResult
|
|||
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
|
||||
|
|
@ -75,7 +74,7 @@ class WarningsMiddleware {
|
|||
preferencesStorage.appRatingLaunchObserver.foundWalletWithFunds()
|
||||
}
|
||||
if (preferencesStorage.appRatingLaunchObserver.isReadyToShow()) {
|
||||
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_DISPLAYED)
|
||||
store.state.globalState.analyticsHandlers?.triggerEvent(AnalyticsEvent.APP_RATING_DISPLAYED)
|
||||
addWarningMessage(WarningMessagesManager.appRatingWarning(), true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,6 +245,17 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
)
|
||||
newState = newState.copy(wallets = wallets)
|
||||
}
|
||||
is WalletAction.SetWalletRent -> {
|
||||
var walletData = newState.getWalletData(action.blockchain)
|
||||
if (walletData == null) {
|
||||
newState
|
||||
} else {
|
||||
walletData = walletData.copy(walletRent = WalletRent(action.minRent, action.rentExempt))
|
||||
newState = newState.copy(
|
||||
wallets = newState.replaceSomeWallets(listOf(walletData))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return newState
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
|
|
@ -27,6 +28,8 @@ import kotlinx.android.synthetic.main.item_popular_token.view.*
|
|||
import kotlinx.android.synthetic.main.layout_balance_error.*
|
||||
import kotlinx.android.synthetic.main.layout_balance_wallet_details.*
|
||||
import kotlinx.android.synthetic.main.layout_wallet_details.*
|
||||
import kotlinx.android.synthetic.main.layout_wallet_details.card_balance
|
||||
import kotlinx.android.synthetic.main.layout_wallet_details_rent.*
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreSubscriber<WalletState> {
|
||||
|
|
@ -124,11 +127,12 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
|
|||
|
||||
handleDialogs(state.walletDialog)
|
||||
handleCurrencyIcon(selectedWallet)
|
||||
handleWalletRent(selectedWallet.walletRent)
|
||||
|
||||
srl_wallet_details.setOnRefreshListener {
|
||||
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
|
||||
store.dispatch(WalletAction.LoadWallet(
|
||||
blockchain = selectedWallet.currency?.blockchain
|
||||
blockchain = selectedWallet.currency.blockchain
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -141,6 +145,18 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
|
|||
btn_sell.show(selectedWallet.tradeCryptoState.sellingAllowed)
|
||||
}
|
||||
|
||||
private fun handleWalletRent(rent: WalletRent?) {
|
||||
val rent = rent.guard {
|
||||
l_rent_warning.hide()
|
||||
return
|
||||
}
|
||||
|
||||
val warningMessage = requireContext().getString(
|
||||
R.string.solana_rent_warning, rent.minRentValue, rent.rentExemptValue)
|
||||
tv_rent_warning_message.text = warningMessage
|
||||
l_rent_warning.show()
|
||||
}
|
||||
|
||||
private fun handleCurrencyIcon(wallet: WalletData) {
|
||||
Picasso.get().loadCurrenciesIcon(
|
||||
imageView = iv_currency,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
|
||||
import com.google.android.play.core.review.ReviewManagerFactory
|
||||
import com.tangem.tap.common.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.getActivity
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
|
|
@ -102,13 +101,14 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) {
|
|||
WarningMessage.Type.AppRating -> {
|
||||
view.group_controls_temporary.hide()
|
||||
view.group_controls_rating.show()
|
||||
val analyticsHandler = store.state.globalState.analyticsHandlers
|
||||
view.btn_close.setOnClickListener {
|
||||
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_DISMISS)
|
||||
analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_DISMISS)
|
||||
store.dispatch(GlobalAction.HideWarningMessage(warning))
|
||||
store.dispatch(WalletAction.Warnings.AppRating.RemindLater)
|
||||
}
|
||||
view.btn_can_be_better.setOnClickListener {
|
||||
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_NEGATIVE)
|
||||
analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_NEGATIVE)
|
||||
store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow)
|
||||
store.dispatch(GlobalAction.HideWarningMessage(warning))
|
||||
store.dispatch(GlobalAction.SendFeedback(RateCanBeBetterEmail()))
|
||||
|
|
@ -116,7 +116,7 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) {
|
|||
view.btn_really_cool.setOnClickListener {
|
||||
val activity = view.context.getActivity() ?: return@setOnClickListener
|
||||
|
||||
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_POSITIVE)
|
||||
analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_POSITIVE)
|
||||
store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow)
|
||||
val reviewManager = ReviewManagerFactory.create(activity)
|
||||
val task = reviewManager.requestReviewFlow()
|
||||
|
|
|
|||
|
|
@ -78,28 +78,38 @@
|
|||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_second_backup_card"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/card_placeholder_new"
|
||||
android:visibility="gone"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:scaleType="fitCenter"
|
||||
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_first_backup_card"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/card_placeholder_new"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_front_card"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/card_placeholder_new"
|
||||
android:transitionName="imv_front_card"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:scaleType="fitCenter"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
|
|
@ -182,6 +192,7 @@
|
|||
app:tabBackground="@drawable/selector_tabs_dots"
|
||||
app:tabIndicator="@null"
|
||||
app:tabPaddingStart="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:tabPaddingEnd="8dp"
|
||||
app:tabGravity="center"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@
|
|||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:theme="@style/ThemeOverlay.MyTheme.Toolbar"
|
||||
app:menu="@menu/wallet_details"
|
||||
app:navigationIcon="@drawable/ic_baseline_arrow_back_24"
|
||||
android:theme="@style/ThemeOverlay.MyTheme.Toolbar"
|
||||
app:title=" " />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
|
@ -46,7 +46,6 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_currency_title"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -79,7 +78,6 @@
|
|||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="tv_currency_subtitle,tv_currency_title" />
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_pending_transaction"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -100,6 +98,18 @@
|
|||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" />
|
||||
|
||||
<include
|
||||
android:id="@+id/l_rent_warning"
|
||||
layout="@layout/layout_wallet_details_rent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/l_wallet_details" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/card_pending_transaction_warning"
|
||||
|
|
@ -131,22 +141,6 @@
|
|||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="l_wallet_details,card_pending_transaction_warning" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_trade"
|
||||
style="@style/TapButtonWithIcon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="33dp"
|
||||
android:text="@string/wallet_button_buy"
|
||||
app:icon="@drawable/ic_arrow_up"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_sell"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/barrier"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier_left_button"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
@ -161,6 +155,22 @@
|
|||
app:barrierDirection="start"
|
||||
app:constraint_referenced_ids="btn_sell, btn_confirm" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_trade"
|
||||
style="@style/TapButtonWithIcon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginBottom="33dp"
|
||||
android:text="@string/wallet_button_buy"
|
||||
app:icon="@drawable/ic_arrow_up"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_sell"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/l_rent_warning"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_sell"
|
||||
style="@style/TapButtonWithIcon"
|
||||
|
|
@ -172,10 +182,10 @@
|
|||
android:text="@string/wallet_button_sell"
|
||||
app:icon="@drawable/ic_arrow_down"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/btn_confirm"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/btn_trade"
|
||||
app:layout_constraintEnd_toStartOf="@id/btn_confirm"
|
||||
app:layout_constraintTop_toBottomOf="@id/barrier"
|
||||
app:layout_constraintTop_toBottomOf="@+id/l_rent_warning"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
|
|
@ -187,12 +197,11 @@
|
|||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="33dp"
|
||||
android:text="@string/wallet_button_send"
|
||||
app:layout_constraintStart_toEndOf="@id/btn_sell"
|
||||
app:icon="@drawable/ic_send"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/barrier"
|
||||
app:layout_constraintStart_toEndOf="@id/btn_sell"
|
||||
app:layout_constraintTop_toBottomOf="@+id/l_rent_warning"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -61,13 +61,16 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/tv_currency_symbol"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/btn_add_token"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_currency"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline"
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@
|
|||
android:layout_marginStart="78dp"
|
||||
android:layout_marginEnd="78dp"
|
||||
android:layout_marginBottom="92dp"
|
||||
android:text="@string/common_create"
|
||||
android:text="@string/common_continue"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue