Updated on 2026-08-14
This commit is contained in:
commit
12bc959fde
112 changed files with 3193 additions and 47273 deletions
|
|
@ -82,10 +82,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-66'
|
||||
// implementation 'com.tangem:blockchain:0.0.1'
|
||||
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-140'
|
||||
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-140'
|
||||
implementation 'com.tangem:blockchain:develop-70'
|
||||
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-142'
|
||||
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-142'
|
||||
|
||||
// WebView
|
||||
implementation "androidx.browser:browser:1.3.0"
|
||||
|
|
@ -128,7 +127,11 @@ dependencies {
|
|||
implementation 'com.squareup.retrofit2:converter-moshi:2.6.0'
|
||||
implementation 'com.squareup.moshi:moshi:1.12.0'
|
||||
implementation "com.squareup.moshi:moshi-kotlin:1.12.0"
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
|
||||
|
||||
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.3"))
|
||||
implementation("com.squareup.okhttp3:okhttp")
|
||||
implementation("com.squareup.okhttp3:logging-interceptor")
|
||||
|
||||
//
|
||||
// //Crypto
|
||||
implementation "com.madgag.spongycastle:core:1.58.0.0"
|
||||
|
|
@ -147,10 +150,16 @@ dependencies {
|
|||
|
||||
//Compose
|
||||
implementation 'androidx.activity:activity-compose:1.4.0'
|
||||
implementation "androidx.compose.material:material:1.1.1"
|
||||
implementation 'androidx.compose.material:material:1.1.1'
|
||||
implementation 'androidx.compose.animation:animation:1.1.1'
|
||||
implementation 'androidx.compose.foundation:foundation:1.1.1'
|
||||
implementation 'androidx.compose.foundation:foundation-layout:1.1.1'
|
||||
implementation 'androidx.compose.ui:ui:1.1.1'
|
||||
implementation 'androidx.compose.ui:ui-tooling:1.1.1'
|
||||
implementation "com.google.accompanist:accompanist-appcompat-theme:0.23.1"
|
||||
implementation "com.google.accompanist:accompanist-appcompat-theme:0.23.0"
|
||||
|
||||
implementation("io.coil-kt:coil:2.0.0-rc01")
|
||||
implementation("io.coil-kt:coil-compose:2.0.0-rc01")
|
||||
|
||||
implementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.6'
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,16 +0,0 @@
|
|||
[
|
||||
{
|
||||
"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,9 +0,0 @@
|
|||
[
|
||||
{
|
||||
"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,65 +0,0 @@
|
|||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,26 +0,0 @@
|
|||
[
|
||||
{
|
||||
"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,16 +0,0 @@
|
|||
[
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://cdn.worldvectorlogo.com/logos/usd-1.svg",
|
||||
"symbol": "FUSD",
|
||||
"name": "Fantom USD",
|
||||
"contractAddress": "0x91ea991bd52EE3C40EdA2509701d905e1Ee54074"
|
||||
},
|
||||
{
|
||||
"decimalCount": 18,
|
||||
"customIconUrl": "https://cryptologos.cc/logos/fantom-ftm-logo.svg?v=003",
|
||||
"symbol": "WFTM",
|
||||
"name": "Wrapped Fantom",
|
||||
"contractAddress": "0xf1277d1Ed8AD466beddF92ef448A132661956621"
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1 +0,0 @@
|
|||
[]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,14 +0,0 @@
|
|||
[
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"symbol": "TCA",
|
||||
"name": "Tangem Coin A",
|
||||
"contractAddress": "22PTNbX31Zuztd6nD82fC8nQdT2hUfWv9XWXKuDkrFqR"
|
||||
},
|
||||
{
|
||||
"decimalCount": 9,
|
||||
"symbol": "TCB",
|
||||
"name": "Tangem Coin B",
|
||||
"contractAddress": "HmSghNPg6KCk711YJA92aPejt8auyFkvmaED6jbHfUs4"
|
||||
}
|
||||
]
|
||||
369
app/src/main/assets/testnet_tokens.json
Normal file
369
app/src/main/assets/testnet_tokens.json
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
{
|
||||
"tokens" : [
|
||||
{
|
||||
"id" : "polygon/test",
|
||||
"symbol" : "MATIC",
|
||||
"name" : "Polygon",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "polygon-pos",
|
||||
"address" : "0x0000000000000000000000000000000000001010",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Dai Stablecoin-DAI",
|
||||
"symbol" : "DAI",
|
||||
"name" : "Dai Stablecoin",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "polygon-pos",
|
||||
"address" : "0xcB1e72786A6eb3b44C2a2429e317c8a2462CFeb1",
|
||||
"decimalCount" : 18
|
||||
},
|
||||
{
|
||||
"networkId" : "binance-smart-chain",
|
||||
"address" : "0xec5dcb5dbf4b114c9d0f65bccab49ec54f6a0867",
|
||||
"decimalCount" : 18
|
||||
},
|
||||
{
|
||||
"networkId" : "binance-smart-chain",
|
||||
"address" : "0x8a9424745056eb399fd19a0ec26a14316684e274",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Dummy ERC20-DERC20",
|
||||
"symbol" : "DERC20",
|
||||
"name" : "Dummy ERC20",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "polygon-pos",
|
||||
"address" : "0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Ether-ETH",
|
||||
"symbol" : "ETH",
|
||||
"name" : "Ether",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "polygon-pos",
|
||||
"address" : "0x714550C2C1Ea08688607D86ed8EeF4f5E4F22323",
|
||||
"decimalCount" : 18
|
||||
},
|
||||
{
|
||||
"networkId" : "binance-smart-chain",
|
||||
"address" : "0xd66c6b4f0be8ce5b39d52e0fd1344c389929b378",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Test Token-TST",
|
||||
"symbol" : "TST",
|
||||
"name" : "Test Token",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "polygon-pos",
|
||||
"address" : "0x2d7882bedcbfddce29ba99965dd3cdf7fcb10a1e",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Tether USD-USDT",
|
||||
"symbol" : "USDT",
|
||||
"name" : "Tether USD",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "polygon-pos",
|
||||
"address" : "0x3813e82e6f7098b9583FC0F33a962D02018B6803",
|
||||
"decimalCount" : 6
|
||||
},
|
||||
{
|
||||
"networkId" : "binance-smart-chain",
|
||||
"address" : "0x337610d27c682e347c9cd60bd4b3b107c9d34ddd",
|
||||
"decimalCount" : 18
|
||||
},
|
||||
{
|
||||
"networkId" : "binance-smart-chain",
|
||||
"address" : "0x7ef95a0fee0dd31b22626fa2e10ee6a223f8a684",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Wrapped Ether-WETH",
|
||||
"symbol" : "WETH",
|
||||
"name" : "Wrapped Ether",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "polygon-pos",
|
||||
"address" : "0xA6FA4fB5f76172d178d61B04b0ecd319C5d1C0aa",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Wrapped Matic-WMATIC",
|
||||
"symbol" : "WMATIC",
|
||||
"name" : "Wrapped Matic",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "polygon-pos",
|
||||
"address" : "0xd0A1E359811322d97991E03f863a0C30C2cF029C",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "solana/test",
|
||||
"symbol" : "SOL",
|
||||
"name" : "Solana",
|
||||
"contracts" : [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Tangem Coin A-TCA",
|
||||
"symbol" : "TCA",
|
||||
"name" : "Tangem Coin A",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "solana",
|
||||
"address" : "22PTNbX31Zuztd6nD82fC8nQdT2hUfWv9XWXKuDkrFqR",
|
||||
"decimalCount" : 9
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Tangem Coin B-TCB",
|
||||
"symbol" : "TCB",
|
||||
"name" : "Tangem Coin B",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "solana",
|
||||
"address" : "HmSghNPg6KCk711YJA92aPejt8auyFkvmaED6jbHfUs4",
|
||||
"decimalCount" : 9
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "binance/test",
|
||||
"symbol" : "BNB",
|
||||
"name" : "Binance",
|
||||
"contracts" : [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Hemster - 452-HEM",
|
||||
"symbol" : "HEM",
|
||||
"name" : "Hemster - 452",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "binancecoin",
|
||||
"address" : "HEM-452",
|
||||
"decimalCount" : 8
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "bsc/test",
|
||||
"symbol" : "BNB",
|
||||
"name" : "Bsc",
|
||||
"contracts" : [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Binance-Peg BTCB Token-BTCB",
|
||||
"symbol" : "BTCB",
|
||||
"name" : "Binance-Peg BTCB Token",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "binance-smart-chain",
|
||||
"address" : "0x6ce8da28e2f864420840cf74474eff5fd80e65b8",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Binance-Peg BUSD Token-BUSD",
|
||||
"symbol" : "BUSD",
|
||||
"name" : "Binance-Peg BUSD Token",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "binance-smart-chain",
|
||||
"address" : "0xed24fc36d5ee211ea25a80239fb8c4cfd80f12ee",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Binance-Peg USDC Token-USDC",
|
||||
"symbol" : "USDC",
|
||||
"name" : "Binance-Peg USDC Token",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "binance-smart-chain",
|
||||
"address" : "0x64544969ed7ebf5f083679233325356ebe738930",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Binance-Peg XRP-XRP",
|
||||
"symbol" : "XRP",
|
||||
"name" : "Binance-Peg XRP",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "binance-smart-chain",
|
||||
"address" : "0xa83575490d7df4e2f47b7d38ef351a2722ca45b9",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "ethereum/test",
|
||||
"symbol" : "ETH",
|
||||
"name" : "Ethereum",
|
||||
"contracts" : [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Weenus-WEENUS",
|
||||
"symbol" : "WEENUS",
|
||||
"name" : "Weenus",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "ethereum",
|
||||
"address" : "0xaFF4481D10270F50f203E0763e2597776068CBc5",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Xeenus-XEENUS",
|
||||
"symbol" : "XEENUS",
|
||||
"name" : "Xeenus",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "ethereum",
|
||||
"address" : "0x022E292b44B5a146F2e8ee36Ff44D3dd863C915c",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Yeenus-YEENUS",
|
||||
"symbol" : "YEENUS",
|
||||
"name" : "Yeenus",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "ethereum",
|
||||
"address" : "0xc6fDe3FD2Cc2b173aEC24cc3f267cb3Cd78a26B7",
|
||||
"decimalCount" : 8
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Zeenus-ZEENUS",
|
||||
"symbol" : "ZEENUS",
|
||||
"name" : "Zeenus",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "ethereum",
|
||||
"address" : "0x1f9061B953bBa0E36BF50F21876132DcF276fC6e",
|
||||
"decimalCount" : 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "avalanche/test",
|
||||
"symbol" : "AVAX",
|
||||
"name" : "Avalanche",
|
||||
"contracts" : [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "The Fuji stablecoin-FUJISTABLE",
|
||||
"symbol" : "FUJISTABLE",
|
||||
"name" : "The Fuji stablecoin",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "avalanche",
|
||||
"address" : "0x2058ec2791dD28b6f67DB836ddf87534F4Bbdf22",
|
||||
"decimalCount" : 6
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "To the Moon-FUJIMOON",
|
||||
"symbol" : "FUJIMOON",
|
||||
"name" : "To the Moon",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "avalanche",
|
||||
"address" : "0x97132C109c6816525F7f338DCb7435E1412A7668",
|
||||
"decimalCount" : 9
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "fantom/test",
|
||||
"symbol" : "FTM",
|
||||
"name" : "Fantom",
|
||||
"contracts" : [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Fantom USD-FUSD",
|
||||
"symbol" : "FUSD",
|
||||
"name" : "Fantom USD",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "fantom",
|
||||
"address" : "0x91ea991bd52EE3C40EdA2509701d905e1Ee54074",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "Wrapped Fantom-WFTM",
|
||||
"symbol" : "WFTM",
|
||||
"name" : "Wrapped Fantom",
|
||||
"contracts" : [
|
||||
{
|
||||
"networkId" : "fantom",
|
||||
"address" : "0xf1277d1Ed8AD466beddF92ef448A132661956621",
|
||||
"decimalCount" : 18
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "bitcoin/test",
|
||||
"symbol" : "BTC",
|
||||
"name" : "Bitcoin",
|
||||
"contracts" : [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "stellar/test",
|
||||
"symbol" : "XLM",
|
||||
"name" : "Stellar",
|
||||
"contracts" : [
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
1
app/src/main/assets/tokens.json
Normal file
1
app/src/main/assets/tokens.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -106,6 +106,7 @@ object TangemSdk {
|
|||
is TangemSdkError.IssuerSignatureLoadingFailed -> TangemSdkError.IssuerSignatureLoadingFailed()
|
||||
is TangemSdkError.BackupFailedFirmware -> TangemSdkError.BackupFailedFirmware()
|
||||
is TangemSdkError.UserForgotTheCode -> TangemSdkError.UserForgotTheCode()
|
||||
is TangemSdkError.BackupFailedIncompatibleBatch -> TangemSdkError.BackupFailedIncompatibleBatch()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.tap.common.compose
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
|
||||
sealed class Keyboard {
|
||||
data class Opened(val height: Int): Keyboard()
|
||||
object Closed : Keyboard()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun keyboardAsState(): State<Keyboard> {
|
||||
val keyboardState: MutableState<Keyboard> = remember { mutableStateOf(Keyboard.Closed) }
|
||||
val view = LocalView.current
|
||||
DisposableEffect(view) {
|
||||
val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener {
|
||||
val rect = Rect()
|
||||
view.getWindowVisibleDisplayFrame(rect)
|
||||
val screenHeight = view.rootView.height
|
||||
val keypadHeight = screenHeight - rect.bottom
|
||||
keyboardState.value = if (keypadHeight > screenHeight * 0.15) {
|
||||
Keyboard.Opened(keypadHeight)
|
||||
} else {
|
||||
Keyboard.Closed
|
||||
}
|
||||
}
|
||||
view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener)
|
||||
|
||||
onDispose {
|
||||
view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener)
|
||||
}
|
||||
}
|
||||
|
||||
return keyboardState
|
||||
}
|
||||
|
|
@ -5,20 +5,54 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.wallet.R
|
||||
|
||||
@DrawableRes
|
||||
fun Blockchain.getIconRes(): Int {
|
||||
fun Blockchain.getRoundIconRes(): Int {
|
||||
return when (this) {
|
||||
Blockchain.Unknown -> R.drawable.shape_circle
|
||||
Blockchain.Ducatus -> R.drawable.ic_ducatus
|
||||
Blockchain.Bitcoin, Blockchain.BitcoinTestnet,-> R.drawable.ic_btc
|
||||
Blockchain.BitcoinCash -> R.drawable.ic_btc_cash
|
||||
Blockchain.Litecoin -> R.drawable.ic_ltc
|
||||
Blockchain.Ethereum, Blockchain.EthereumTestnet, -> R.drawable.ic_eth
|
||||
Blockchain.RSK -> R.drawable.ic_rsk
|
||||
Blockchain.Cardano, Blockchain.CardanoShelley -> R.drawable.ic_cardano
|
||||
Blockchain.Binance, Blockchain.BinanceTestnet -> R.drawable.ic_binance
|
||||
Blockchain.Tezos -> R.drawable.ic_tezos
|
||||
Blockchain.XRP -> R.drawable.ic_xrp
|
||||
Blockchain.Stellar -> R.drawable.ic_stellar
|
||||
else -> R.drawable.shape_circle
|
||||
Blockchain.Bitcoin, Blockchain.BitcoinTestnet,-> R.drawable.ic_bitcoin_round
|
||||
Blockchain.BitcoinCash -> R.drawable.ic_bitcoin_cash_round
|
||||
Blockchain.Litecoin -> R.drawable.ic_litecoin_round
|
||||
Blockchain.Ethereum, Blockchain.EthereumTestnet, -> R.drawable.ic_eth_round
|
||||
Blockchain.RSK -> R.drawable.ic_rsk_round
|
||||
Blockchain.Cardano, Blockchain.CardanoShelley -> R.drawable.ic_cardano_round
|
||||
Blockchain.Tezos -> R.drawable.ic_tezos_round
|
||||
Blockchain.XRP -> R.drawable.ic_xrp_round
|
||||
Blockchain.Stellar -> R.drawable.ic_stellar_round
|
||||
Blockchain.Avalanche, Blockchain.AvalancheTestnet -> R.drawable.ic_avalanche_round
|
||||
Blockchain.Polygon, Blockchain.PolygonTestnet -> R.drawable.ic_polygon_round
|
||||
Blockchain.Solana, Blockchain.SolanaTestnet -> R.drawable.ic_solana_round
|
||||
Blockchain.Fantom, Blockchain.FantomTestnet -> R.drawable.ic_fantom_round
|
||||
Blockchain.BSC, Blockchain.BSCTestnet, Blockchain.Binance, Blockchain.BinanceTestnet -> R.drawable.ic_bsc_round
|
||||
else -> R.drawable.ic_tangem_logo
|
||||
}
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
fun Blockchain.getGreyedOutIconRes(): Int {
|
||||
return when (this) {
|
||||
// Blockchain.Ducatus -> R.drawable.ic_ducatus
|
||||
Blockchain.Bitcoin, Blockchain.BitcoinTestnet,-> R.drawable.ic_bitcoin_no_color
|
||||
Blockchain.BitcoinCash -> R.drawable.ic_bitcoin_cash_no_color
|
||||
Blockchain.Litecoin -> R.drawable.ic_litecoin_no_color
|
||||
Blockchain.Ethereum, Blockchain.EthereumTestnet, -> R.drawable.ic_eth_no_color
|
||||
Blockchain.RSK -> R.drawable.ic_rsk_no_color
|
||||
Blockchain.Cardano, Blockchain.CardanoShelley -> R.drawable.ic_cardano_no_color
|
||||
Blockchain.Tezos -> R.drawable.ic_tezos_no_color
|
||||
Blockchain.XRP -> R.drawable.ic_xrp_no_color
|
||||
Blockchain.Stellar -> R.drawable.ic_stellar_no_color
|
||||
Blockchain.Avalanche, Blockchain.AvalancheTestnet -> R.drawable.ic_avalanche_no_color
|
||||
Blockchain.Polygon, Blockchain.PolygonTestnet -> R.drawable.ic_polygon_no_color
|
||||
Blockchain.Solana, Blockchain.SolanaTestnet -> R.drawable.ic_solana_no_color
|
||||
Blockchain.Fantom, Blockchain.FantomTestnet -> R.drawable.ic_fantom_no_color
|
||||
Blockchain.BSC, Blockchain.BSCTestnet, Blockchain.Binance, Blockchain.BinanceTestnet -> R.drawable.ic_bsc_no_color
|
||||
else -> R.drawable.ic_tangem_logo
|
||||
}
|
||||
}
|
||||
|
||||
fun Blockchain.getNetworkName(): String {
|
||||
return when (this) {
|
||||
Blockchain.Ethereum, Blockchain.EthereumTestnet -> "ERC20"
|
||||
Blockchain.BSC, Blockchain.BSCTestnet -> "BEP20"
|
||||
Blockchain.Binance, Blockchain.BinanceTestnet -> "BEP2"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.graphics.*
|
||||
import android.net.Uri
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
import com.squareup.picasso.Callback
|
||||
|
|
@ -11,6 +10,8 @@ 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.tap.domain.tokens.getIconUrl
|
||||
import com.tangem.tap.domain.tokens.toNetworkId
|
||||
import com.tangem.wallet.R
|
||||
|
||||
fun Picasso.loadCurrenciesIcon(
|
||||
|
|
@ -20,10 +21,11 @@ fun Picasso.loadCurrenciesIcon(
|
|||
blockchain: Blockchain,
|
||||
) {
|
||||
|
||||
val url = if (token != null) {
|
||||
token.getCustomIconUrl()?.let { Uri.parse(it) } ?: IconsUtil.getTokenIconUri(blockchain, token)
|
||||
val url: String? = if (token != null) {
|
||||
token.id?.let { getIconUrl(it) } ?: token.getCustomIconUrl()
|
||||
?: IconsUtil.getTokenIconUri(blockchain, token)?.toString()
|
||||
} else {
|
||||
IconsUtil.getBlockchainIconUri(blockchain)
|
||||
getIconUrl(blockchain.toNetworkId())
|
||||
}
|
||||
|
||||
imageView.setImageDrawable(null)
|
||||
|
|
@ -39,7 +41,7 @@ fun Picasso.loadCurrenciesIcon(
|
|||
}
|
||||
url != null -> {
|
||||
if (token != null) {
|
||||
setTokenImage(imageView, textView, token)
|
||||
setTokenImage(imageView, textView, token, blockchain)
|
||||
}
|
||||
this.load(url)
|
||||
.transform(RoundedCornersTransform())
|
||||
|
|
@ -76,7 +78,7 @@ private fun setOfflineCurrencyImage(
|
|||
) {
|
||||
when (token) {
|
||||
null -> setBlockchainImage(imageView, textView, blockchain)
|
||||
else -> setTokenImage(imageView, textView, token)
|
||||
else -> setTokenImage(imageView, textView, token, blockchain)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +87,7 @@ private fun setBlockchainImage(
|
|||
textView: TextView,
|
||||
blockchain: Blockchain,
|
||||
) {
|
||||
imageView.setImageResource(blockchain.getIconRes())
|
||||
imageView.setImageResource(blockchain.getRoundIconRes())
|
||||
imageView.colorFilter = null
|
||||
if (blockchain.isTestnet()) imageView.saturation = 0f
|
||||
textView.text = null
|
||||
|
|
@ -95,9 +97,10 @@ private fun setTokenImage(
|
|||
imageView: ImageFilterView,
|
||||
textView: TextView,
|
||||
token: Token,
|
||||
tokenBlockchain: Blockchain
|
||||
) {
|
||||
imageView.setImageResource(R.drawable.shape_circle)
|
||||
if (token.blockchain.isTestnet()) {
|
||||
if (tokenBlockchain.isTestnet()) {
|
||||
imageView.saturation = 0f
|
||||
} else {
|
||||
imageView.setColorFilter(token.getColor())
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import android.content.res.Resources
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
|
@ -19,6 +20,7 @@ import androidx.fragment.app.Fragment
|
|||
import com.google.android.material.card.MaterialCardView
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
|
||||
|
||||
fun Fragment.getDrawable(@DrawableRes drawableResId: Int): Drawable? {
|
||||
return ContextCompat.getDrawable(requireContext(), drawableResId)
|
||||
}
|
||||
|
|
@ -94,6 +96,13 @@ fun Context.dpToPixels(dp: Int): Int =
|
|||
TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), this.resources.displayMetrics
|
||||
).toInt()
|
||||
|
||||
|
||||
fun Context.pixelsToDp(pixels: Int): Int {
|
||||
return (pixels.toFloat() /
|
||||
(resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT))
|
||||
.toInt()
|
||||
}
|
||||
|
||||
tailrec fun Context?.getActivity(): Activity? = this as? Activity
|
||||
?: (this as? ContextWrapper)?.baseContext?.getActivity()
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.services.Result
|
|||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.ThrottlerWithValues
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
|
|
@ -19,6 +20,7 @@ import com.tangem.tap.domain.extensions.isMultiwalletAllowed
|
|||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.domain.extensions.makeWalletManagersForApp
|
||||
import com.tangem.tap.domain.tokens.CardCurrencies
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
import com.tangem.tap.features.wallet.models.PendingTransactionType
|
||||
import com.tangem.tap.features.wallet.models.getPendingTransactions
|
||||
|
|
@ -46,6 +48,7 @@ class TapWalletManager {
|
|||
|
||||
suspend fun loadWalletData(walletManager: WalletManager) {
|
||||
val blockchain = walletManager.wallet.blockchain
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
val result = if (walletManagersThrottler.isStillThrottled(blockchain)) {
|
||||
walletManagersThrottler.geValue(blockchain)!!
|
||||
} else {
|
||||
|
|
@ -54,21 +57,26 @@ class TapWalletManager {
|
|||
when (result) {
|
||||
is Result.Success -> {
|
||||
checkForRentWarning(walletManager)
|
||||
dispatchOnMain(WalletAction.LoadWallet.Success(result.data))
|
||||
dispatchOnMain(WalletAction.LoadWallet.Success(result.data, blockchainNetwork))
|
||||
}
|
||||
is Result.Failure -> {
|
||||
when (result.error) {
|
||||
is TapError.WalletManagerUpdate.NoAccountError -> {
|
||||
dispatchOnMain(WalletAction.LoadWallet.NoAccount(
|
||||
walletManager.wallet,
|
||||
(result.error as TapError.WalletManagerUpdate.NoAccountError).customMessage
|
||||
))
|
||||
dispatchOnMain(
|
||||
WalletAction.LoadWallet.NoAccount(
|
||||
walletManager.wallet,
|
||||
blockchainNetwork,
|
||||
(result.error as TapError.WalletManagerUpdate.NoAccountError).customMessage
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
dispatchOnMain(WalletAction.LoadWallet.Failure(
|
||||
walletManager.wallet,
|
||||
result.error.localizedMessage
|
||||
))
|
||||
dispatchOnMain(
|
||||
WalletAction.LoadWallet.Failure(
|
||||
walletManager.wallet,
|
||||
result.error.localizedMessage
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,8 +93,8 @@ class TapWalletManager {
|
|||
|
||||
suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName, wallet: Wallet) {
|
||||
val currencies = wallet.getTokens()
|
||||
.map { Currency.Token(it) }
|
||||
.plus(Currency.Blockchain(wallet.blockchain))
|
||||
.map { Currency.Token(it, wallet.blockchain, wallet.publicKey.derivationPath?.rawPath) }
|
||||
.plus(Currency.Blockchain(wallet.blockchain, wallet.publicKey.derivationPath?.rawPath))
|
||||
loadFiatRate(fiatCurrency, currencies)
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +138,8 @@ class TapWalletManager {
|
|||
configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled)
|
||||
configManager?.turnOff(ConfigManager.isTopUpEnabled)
|
||||
} else if (blockchain == Blockchain.Bitcoin
|
||||
|| data.walletData?.blockchain == Blockchain.Bitcoin.id) {
|
||||
|| data.walletData?.blockchain == Blockchain.Bitcoin.id
|
||||
) {
|
||||
configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled)
|
||||
configManager?.resetToDefault(ConfigManager.isTopUpEnabled)
|
||||
} else {
|
||||
|
|
@ -161,8 +170,10 @@ class TapWalletManager {
|
|||
loadMultiWalletData(data, blockchain, primaryWalletManager)
|
||||
} else {
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager),
|
||||
WalletAction.MultiWallet.AddBlockchains(listOf(blockchain))
|
||||
WalletAction.MultiWallet.AddBlockchains(
|
||||
listOf(BlockchainNetwork.fromWalletManager(primaryWalletManager)),
|
||||
listOf(primaryWalletManager)
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -177,32 +188,40 @@ class TapWalletManager {
|
|||
}
|
||||
|
||||
private suspend fun loadMultiWalletData(
|
||||
scanResponse: ScanResponse, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager?
|
||||
scanResponse: ScanResponse,
|
||||
primaryBlockchain: Blockchain?,
|
||||
primaryWalletManager: WalletManager?
|
||||
) {
|
||||
val primaryTokens = primaryWalletManager?.cardTokens?.toList() ?: emptyList()
|
||||
val savedCurrencies = currenciesRepository.loadCardCurrencies(scanResponse.card.cardId)
|
||||
val savedCurrencies = currenciesRepository.loadSavedCurrencies(scanResponse.card.cardId, scanResponse.card.derivationStyle)
|
||||
|
||||
if (savedCurrencies == null) {
|
||||
if (savedCurrencies.isEmpty()) {
|
||||
if (primaryBlockchain != null && primaryWalletManager != null) {
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(primaryWalletManager)
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.SaveCurrencies(
|
||||
CardCurrencies(blockchains = listOf(primaryBlockchain), tokens = primaryTokens)
|
||||
WalletAction.MultiWallet.SaveCurrencies(listOf(blockchainNetwork)),
|
||||
WalletAction.MultiWallet.AddBlockchains(
|
||||
listOf(blockchainNetwork),
|
||||
listOf(primaryWalletManager)
|
||||
),
|
||||
WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager),
|
||||
WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain)),
|
||||
WalletAction.MultiWallet.AddTokens(primaryTokens.toList())
|
||||
WalletAction.MultiWallet.AddTokens(primaryTokens.toList(), blockchainNetwork)
|
||||
)
|
||||
|
||||
} else {
|
||||
val blockchains = listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
val walletManagers = walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains.toList())
|
||||
val derivationStyle = scanResponse.card.derivationStyle
|
||||
val blockchainNetworks = listOf(
|
||||
BlockchainNetwork(Blockchain.Bitcoin, scanResponse.card),
|
||||
BlockchainNetwork(Blockchain.Ethereum, scanResponse.card)
|
||||
)
|
||||
|
||||
val walletManagers = walletManagerFactory.makeWalletManagersForApp(
|
||||
scanResponse,
|
||||
blockchainNetworks
|
||||
)
|
||||
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.SaveCurrencies(CardCurrencies(
|
||||
blockchains = blockchains,
|
||||
tokens = emptyList()
|
||||
)),
|
||||
WalletAction.MultiWallet.AddWalletManagers(walletManagers),
|
||||
WalletAction.MultiWallet.AddBlockchains(blockchains.toList()),
|
||||
WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks),
|
||||
WalletAction.MultiWallet.AddBlockchains(blockchainNetworks, walletManagers),
|
||||
)
|
||||
}
|
||||
dispatchOnMain(
|
||||
|
|
@ -210,22 +229,25 @@ class TapWalletManager {
|
|||
WalletAction.MultiWallet.FindTokensInUse,
|
||||
)
|
||||
} else {
|
||||
val blockchains = savedCurrencies.blockchains.toList()
|
||||
val walletManagers = if (
|
||||
primaryTokens.isNotEmpty() &&
|
||||
primaryWalletManager != null && primaryBlockchain != null
|
||||
primaryWalletManager != null &&
|
||||
primaryBlockchain != null
|
||||
) {
|
||||
val blockchainsWithoutPrimary = blockchains.filterNot { it == primaryBlockchain }
|
||||
walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchainsWithoutPrimary)
|
||||
.plus(primaryWalletManager)
|
||||
val blockchainsWithoutPrimary = savedCurrencies.filterNot { it.blockchain == primaryBlockchain }
|
||||
walletManagerFactory.makeWalletManagersForApp(
|
||||
scanResponse,
|
||||
blockchainsWithoutPrimary
|
||||
).plus(primaryWalletManager)
|
||||
} else {
|
||||
walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains)
|
||||
walletManagerFactory.makeWalletManagersForApp(scanResponse, savedCurrencies)
|
||||
}
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.AddWalletManagers(walletManagers),
|
||||
WalletAction.MultiWallet.AddBlockchains(blockchains),
|
||||
WalletAction.MultiWallet.AddTokens(savedCurrencies.tokens.toList()),
|
||||
WalletAction.MultiWallet.AddBlockchains(savedCurrencies, walletManagers),
|
||||
)
|
||||
savedCurrencies.map {
|
||||
dispatchOnMain(WalletAction.MultiWallet.AddTokens(it.tokens, it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -269,10 +291,12 @@ class TapWalletManager {
|
|||
|
||||
when (val result = rentProvider.minimalBalanceForRentExemption()) {
|
||||
is com.tangem.blockchain.extensions.Result.Success -> {
|
||||
fun isNeedToShowWarning(balance: BigDecimal, rentExempt: BigDecimal): Boolean = balance < rentExempt
|
||||
fun isNeedToShowWarning(balance: BigDecimal, rentExempt: BigDecimal): Boolean =
|
||||
balance < rentExempt
|
||||
|
||||
val balance = walletManager.wallet.fundsAvailable(AmountType.Coin)
|
||||
val outgoingTxs = walletManager.wallet.getPendingTransactions(PendingTransactionType.Outgoing)
|
||||
val outgoingTxs =
|
||||
walletManager.wallet.getPendingTransactions(PendingTransactionType.Outgoing)
|
||||
val rentExempt = result.data
|
||||
val show = if (outgoingTxs.isEmpty()) {
|
||||
isNeedToShowWarning(balance, rentExempt)
|
||||
|
|
@ -284,11 +308,13 @@ class TapWalletManager {
|
|||
if (!show) return
|
||||
|
||||
val currency = walletManager.wallet.blockchain.currency
|
||||
dispatchOnMain(WalletAction.SetWalletRent(
|
||||
blockchain = walletManager.wallet.blockchain,
|
||||
minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency"),
|
||||
rentExempt = ("${rentExempt.stripZeroPlainString()} $currency")
|
||||
))
|
||||
dispatchOnMain(
|
||||
WalletAction.SetWalletRent(
|
||||
blockchain = BlockchainNetwork.fromWalletManager(walletManager),
|
||||
minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency"),
|
||||
rentExempt = ("${rentExempt.stripZeroPlainString()} $currency")
|
||||
)
|
||||
)
|
||||
}
|
||||
is com.tangem.blockchain.extensions.Result.Failure -> {}
|
||||
}
|
||||
|
|
|
|||
91
app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt
Normal file
91
app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.tap.domain.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.tap.domain.TapWorkarounds.isTangemNote
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tap.domain.twins.isTangemTwin
|
||||
import java.util.*
|
||||
|
||||
object TapWorkarounds {
|
||||
|
||||
fun isStart2CoinIssuer(cardIssuer: String?): Boolean {
|
||||
return cardIssuer?.lowercase(Locale.US) == START_2_COIN_ISSUER
|
||||
}
|
||||
|
||||
val Card.isStart2Coin: Boolean
|
||||
get() = isStart2CoinIssuer(issuer.name)
|
||||
|
||||
val Card.isTestCard: Boolean
|
||||
get() = batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH)
|
||||
|
||||
val Card.useOldStyleDerivation: Boolean
|
||||
get() = batchId == "AC01" || batchId == "AC02" || batchId == "CB95"
|
||||
|
||||
val Card.derivationStyle: DerivationStyle?
|
||||
get() = if (!settings.isHDWalletAllowed) {
|
||||
null
|
||||
} else if (useOldStyleDerivation) {
|
||||
DerivationStyle.LEGACY
|
||||
} else {
|
||||
DerivationStyle.NEW
|
||||
}
|
||||
|
||||
fun Card.isExcluded(): Boolean {
|
||||
val excludedBatch = excludedBatches.contains(batchId)
|
||||
val excludedIssuerName = excludedIssuers.contains(issuer.name.uppercase(Locale.ROOT))
|
||||
return excludedBatch || excludedIssuerName
|
||||
}
|
||||
|
||||
fun Card.isNotSupportedInThatRelease(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
@Deprecated("Use ScanResponse.isTangemNote")
|
||||
fun isTangemNote(card: Card): Boolean = tangemNoteBatches.contains(card.batchId)
|
||||
|
||||
fun isTangemWalletBatch(card: Card): Boolean = tangemWalletBatches.contains(card.batchId)
|
||||
|
||||
fun getTangemNoteBlockchain(card: Card): Blockchain? = tangemNoteBatches[card.batchId]
|
||||
|
||||
private const val START_2_COIN_ISSUER = "start2coin"
|
||||
private const val TEST_CARD_BATCH = "99FF"
|
||||
private const val TEST_CARD_ID_STARTS_WITH = "FF99"
|
||||
|
||||
private val excludedBatches = listOf(
|
||||
"0027",
|
||||
"0030",
|
||||
"0031",
|
||||
"0035"
|
||||
)
|
||||
|
||||
private val excludedIssuers = listOf(
|
||||
"TTM BANK"
|
||||
)
|
||||
|
||||
private val tangemWalletBatches = listOf("AC01")
|
||||
|
||||
private val tangemNoteBatches = mapOf(
|
||||
"AB01" to Blockchain.Bitcoin,
|
||||
"AB02" to Blockchain.Ethereum,
|
||||
"AB03" to Blockchain.CardanoShelley,
|
||||
"AB04" to Blockchain.Dogecoin,
|
||||
"AB05" to Blockchain.BSC,
|
||||
"AB06" to Blockchain.XRP,
|
||||
"AB07" to Blockchain.Bitcoin,
|
||||
"AB08" to Blockchain.Ethereum,
|
||||
)
|
||||
}
|
||||
|
||||
val DELAY_SDK_DIALOG_CLOSE = 1400L
|
||||
|
||||
val Card.isMultiwalletAllowed: Boolean
|
||||
get() {
|
||||
return !isTangemTwin() && !isStart2Coin && !isTangemNote(this)
|
||||
&& (firmwareVersion >= FirmwareVersion.MultiWalletAvailable ||
|
||||
getSingleWallet()?.curve == EllipticCurve.Secp256k1)
|
||||
}
|
||||
|
|
@ -1,27 +1,32 @@
|
|||
package com.tangem.tap.domain.extensions
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.CardWallet
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toMapKey
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagerForApp(
|
||||
scanResponse: ScanResponse,
|
||||
blockchain: Blockchain
|
||||
blockchain: Blockchain,
|
||||
derivationParams: DerivationParams?
|
||||
): WalletManager? {
|
||||
val card = scanResponse.card
|
||||
if (card.isTestCard && blockchain.getTestnetVersion() == null) return null
|
||||
val supportedCurves = blockchain.getSupportedCurves() ?: return null
|
||||
val supportedCurves = blockchain.getSupportedCurves()
|
||||
|
||||
val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) }
|
||||
val wallet = selectWallet(wallets) ?: return null
|
||||
|
||||
val environmentBlockchain = if (card.isTestCard) blockchain.getTestnetVersion()!! else blockchain
|
||||
val environmentBlockchain =
|
||||
if (card.isTestCard) blockchain.getTestnetVersion()!! else blockchain
|
||||
|
||||
val seedKey = wallet.extendedPublicKey
|
||||
return when {
|
||||
|
|
@ -32,16 +37,21 @@ fun WalletManagerFactory.makeWalletManagerForApp(
|
|||
environmentBlockchain, wallet.curve
|
||||
)
|
||||
}
|
||||
seedKey != null -> {
|
||||
seedKey != null && derivationParams != null -> {
|
||||
val derivedKeys = scanResponse.derivedKeys[wallet.publicKey.toMapKey()]
|
||||
val derivedKey = derivedKeys?.get(blockchain.derivationPath())
|
||||
val derivationPath = when (derivationParams) {
|
||||
is DerivationParams.Default -> blockchain.derivationPath(derivationParams.style)
|
||||
is DerivationParams.Custom -> derivationParams.path
|
||||
}
|
||||
val derivedKey = derivedKeys?.get(derivationPath)
|
||||
?: return null
|
||||
|
||||
makeWalletManager(
|
||||
cardId = card.cardId,
|
||||
blockchain = environmentBlockchain,
|
||||
seedKey = wallet.publicKey,
|
||||
derivedKey = derivedKey
|
||||
derivedKey = derivedKey,
|
||||
derivation = derivationParams
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
|
|
@ -55,12 +65,44 @@ fun WalletManagerFactory.makeWalletManagerForApp(
|
|||
}
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagerForApp(
|
||||
scanResponse: ScanResponse, blockchainNetwork: BlockchainNetwork
|
||||
): WalletManager? {
|
||||
return makeWalletManagerForApp(
|
||||
scanResponse,
|
||||
blockchain = blockchainNetwork.blockchain,
|
||||
derivationParams = getDerivationParams(blockchainNetwork.derivationPath, scanResponse.card)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getDerivationParams(derivationPath: String?, card: Card): DerivationParams? {
|
||||
return derivationPath?.let {
|
||||
DerivationParams.Custom(
|
||||
DerivationPath(it)
|
||||
)
|
||||
} ?: if (!card.settings.isHDWalletAllowed) {
|
||||
null
|
||||
} else if (card.useOldStyleDerivation) {
|
||||
DerivationParams.Default(DerivationStyle.LEGACY)
|
||||
} else {
|
||||
DerivationParams.Default(DerivationStyle.NEW)
|
||||
}
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagerForApp(
|
||||
scanResponse: ScanResponse, currency: Currency
|
||||
): WalletManager? {
|
||||
return makeWalletManagerForApp(
|
||||
scanResponse,
|
||||
blockchain = currency.blockchain,
|
||||
derivationParams = getDerivationParams(currency.derivationPath, scanResponse.card)
|
||||
)
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagersForApp(
|
||||
scanResponse: ScanResponse, blockchains: List<Blockchain>,
|
||||
scanResponse: ScanResponse, blockchains: List<BlockchainNetwork>,
|
||||
): List<WalletManager> {
|
||||
val isTestCard = scanResponse.card.isTestCard
|
||||
val filteredBlockchains = blockchains.mapNotNull { if (isTestCard) it.getTestnetVersion() else it }
|
||||
return filteredBlockchains.mapNotNull { makeWalletManagerForApp(scanResponse, it) }
|
||||
return blockchains.mapNotNull { this.makeWalletManagerForApp(scanResponse, it) }
|
||||
}
|
||||
|
||||
fun WalletManagerFactory.makePrimaryWalletManager(
|
||||
|
|
@ -71,7 +113,12 @@ fun WalletManagerFactory.makePrimaryWalletManager(
|
|||
} else {
|
||||
scanResponse.getBlockchain()
|
||||
}
|
||||
return makeWalletManagerForApp(scanResponse, blockchain)
|
||||
val derivationParams = getDerivationParams(null, scanResponse.card)
|
||||
return makeWalletManagerForApp(
|
||||
scanResponse = scanResponse,
|
||||
blockchain = blockchain,
|
||||
derivationParams = derivationParams
|
||||
)
|
||||
}
|
||||
|
||||
private fun selectWallet(wallets: List<CardWallet>): CardWallet? {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ 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.domain.common.ProductType
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.TapWorkarounds.getTangemNoteBlockchain
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.domain.tasks.product.CreateWalletsTask
|
||||
import com.tangem.tap.domain.tasks.product.ProductCommandProcessor
|
||||
import com.tangem.tap.domain.tasks.product.getCurvesForNonCreatedWallets
|
||||
|
|
@ -177,7 +181,7 @@ private class CreateWalletTangemWallet : ProductCommandProcessor<CreateProductWa
|
|||
val blockchainsForCurve = getBlockchains(response.cardId).filter {
|
||||
it.getSupportedCurves().contains(response.wallet.curve)
|
||||
}
|
||||
val derivationPaths = blockchainsForCurve.mapNotNull { it.derivationPath() }
|
||||
val derivationPaths = blockchainsForCurve.mapNotNull { it.derivationPath(card.derivationStyle) }
|
||||
if (derivationPaths.isNotEmpty()) {
|
||||
map[response.wallet.publicKey.toMapKey()] = derivationPaths
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.tap.domain.tasks.product
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.card.WalletData
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.TangemError
|
||||
|
|
@ -25,11 +27,57 @@ import com.tangem.operations.backup.StartPrimaryCardLinkingTask
|
|||
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
|
||||
import com.tangem.tap.domain.TapSdkError
|
||||
import com.tangem.domain.common.TapWorkarounds
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.TapWorkarounds.getTangemNoteBlockchain
|
||||
import com.tangem.domain.common.TapWorkarounds.isExcluded
|
||||
import com.tangem.domain.common.TapWorkarounds.isNotSupportedInThatRelease
|
||||
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
|
||||
import com.tangem.tap.domain.extensions.getPrimaryCurve
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.domain.tokens.CurrenciesRepository
|
||||
import com.tangem.tap.preferencesStorage
|
||||
|
||||
data class 1ScanResponse(
|
||||
val card: Card,
|
||||
val productType: ProductType,
|
||||
val walletData: WalletData?,
|
||||
val secondTwinPublicKey: String? = null,
|
||||
val derivedKeys: Map<KeyWalletPublicKey, ExtendedPublicKeysMap> = mapOf(),
|
||||
val primaryCard: PrimaryCard? = null
|
||||
) : CommandResponse {
|
||||
|
||||
fun getBlockchain(): Blockchain {
|
||||
if (productType == ProductType.Note) return getTangemNoteBlockchain(card)
|
||||
?: return Blockchain.Unknown
|
||||
val blockchainName: String = walletData?.blockchain ?: return Blockchain.Unknown
|
||||
return Blockchain.fromId(blockchainName)
|
||||
}
|
||||
|
||||
fun getPrimaryToken(): Token? {
|
||||
val cardToken = walletData?.token ?: return null
|
||||
return Token(
|
||||
cardToken.name,
|
||||
cardToken.symbol,
|
||||
cardToken.contractAddress,
|
||||
cardToken.decimals,
|
||||
)
|
||||
}
|
||||
|
||||
fun isTangemNote(): Boolean = productType == ProductType.Note
|
||||
fun isTangemWallet(): Boolean = productType == ProductType.Wallet
|
||||
fun isTangemTwins(): Boolean = productType == ProductType.Twins
|
||||
|
||||
fun supportsHdWallet(): Boolean = card.settings.isHDWalletAllowed
|
||||
fun supportsBackup(): Boolean = card.settings.isBackupAllowed
|
||||
|
||||
fun twinsIsTwinned(): Boolean =
|
||||
card.isTangemTwins() && walletData != null && secondTwinPublicKey != null
|
||||
}
|
||||
|
||||
typealias KeyWalletPublicKey = ByteArrayKey
|
||||
|
||||
class ScanProductTask(
|
||||
val card: Card? = null,
|
||||
private val currenciesRepository: CurrenciesRepository?,
|
||||
|
|
@ -223,28 +271,38 @@ private class ScanWalletProcessor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getBlockchainsToDerive(card: Card): List<Blockchain> {
|
||||
private fun getBlockchainsToDerive(card: Card): List<BlockchainNetwork> {
|
||||
val currenciesRepository = currenciesRepository ?: return emptyList()
|
||||
val cardCurrencies = currenciesRepository.loadCardCurrencies(card.cardId)
|
||||
val cardCurrencies = currenciesRepository.loadSavedCurrencies(card.cardId, card.derivationStyle).toMutableList()
|
||||
|
||||
val blockchainsToDerive = if (cardCurrencies == null) {
|
||||
mutableListOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
} else {
|
||||
val tokenBlockchains = cardCurrencies.tokens.map { it.blockchain }
|
||||
(cardCurrencies.blockchains + tokenBlockchains).toMutableList()
|
||||
val blockchainsToDerive = cardCurrencies.ifEmpty {
|
||||
mutableListOf(
|
||||
BlockchainNetwork(Blockchain.Bitcoin, card),
|
||||
BlockchainNetwork(Blockchain.Ethereum, card))
|
||||
}
|
||||
|
||||
if (card.settings.isHDWalletAllowed) {
|
||||
blockchainsToDerive.addAll(
|
||||
listOf(
|
||||
Blockchain.Ethereum,
|
||||
Blockchain.Binance,
|
||||
Blockchain.EthereumTestnet
|
||||
BlockchainNetwork(Blockchain.Ethereum, card),
|
||||
BlockchainNetwork(Blockchain.Binance, card),
|
||||
BlockchainNetwork(Blockchain.EthereumTestnet, card)
|
||||
)
|
||||
)
|
||||
}
|
||||
if (additionalBlockchainsToDerive != null) {
|
||||
blockchainsToDerive.addAll(additionalBlockchainsToDerive)
|
||||
blockchainsToDerive.addAll(additionalBlockchainsToDerive.map { BlockchainNetwork(it, card) })
|
||||
}
|
||||
if (!card.useOldStyleDerivation) {
|
||||
blockchainsToDerive.removeAll(
|
||||
listOf(
|
||||
Blockchain.BSC, Blockchain.BSCTestnet,
|
||||
Blockchain.Polygon, Blockchain.PolygonTestnet,
|
||||
Blockchain.RSK,
|
||||
Blockchain.Fantom, Blockchain.FantomTestnet,
|
||||
Blockchain.Avalanche, Blockchain.AvalancheTestnet,
|
||||
).map { BlockchainNetwork(it, card) }
|
||||
)
|
||||
}
|
||||
return blockchainsToDerive.distinct()
|
||||
}
|
||||
|
|
@ -254,13 +312,13 @@ private class ScanWalletProcessor(
|
|||
val derivations = mutableMapOf<ByteArrayKey, List<DerivationPath>>()
|
||||
|
||||
blockchains.forEach { blockchain ->
|
||||
val curve = blockchain.getPrimaryCurve()
|
||||
val curve = blockchain.blockchain.getPrimaryCurve()
|
||||
|
||||
val wallet = card.wallets.firstOrNull { it.curve == curve } ?: return@forEach
|
||||
if (wallet.chainCode == null) return@forEach
|
||||
|
||||
val key = wallet.publicKey.toMapKey()
|
||||
val path = blockchain.derivationPath()
|
||||
val path = blockchain.derivationPath?.let { DerivationPath(it) }
|
||||
if (path != null) {
|
||||
val addedDerivations = derivations[key]
|
||||
if (addedDerivations != null) {
|
||||
|
|
|
|||
|
|
@ -7,15 +7,19 @@ import com.squareup.moshi.JsonAdapter
|
|||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.network.common.MoshiConverter
|
||||
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.TapWorkarounds.derivationStyle
|
||||
import com.tangem.tap.domain.extensions.setCustomIconUrl
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
|
||||
class CurrenciesRepository(val context: Application) {
|
||||
|
||||
|
|
@ -29,52 +33,48 @@ class CurrenciesRepository(val context: Application) {
|
|||
private val obsoleteTokensAdapter: JsonAdapter<List<ObsoleteTokenDao>> = moshi.adapter(
|
||||
Types.newParameterizedType(List::class.java, ObsoleteTokenDao::class.java)
|
||||
)
|
||||
private val currenciesAdapter: JsonAdapter<CurrenciesFromJson> =
|
||||
moshi.adapter(CurrenciesFromJson::class.java)
|
||||
|
||||
fun loadCardCurrencies(cardId: String): CardCurrencies? {
|
||||
val blockchains = loadSavedBlockchains(cardId).toMutableSet()
|
||||
if (DemoHelper.isDemoCardId(cardId)) {
|
||||
blockchains.addAll(DemoHelper.config.demoBlockchains)
|
||||
private val blockchainNetworkAdapter: JsonAdapter<List<BlockchainNetwork>> =
|
||||
moshi.adapter(Types.newParameterizedType(List::class.java, BlockchainNetwork::class.java))
|
||||
|
||||
fun saveUpdatedCurrency(cardId: String, blockchainNetwork: BlockchainNetwork) {
|
||||
var changed = false
|
||||
val currencies = loadSavedCurrencies(cardId).map {
|
||||
if (it == blockchainNetwork) {
|
||||
changed = true
|
||||
blockchainNetwork
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
if (blockchains.isEmpty()) return null
|
||||
|
||||
return CardCurrencies(loadSavedTokens(cardId), blockchains.toList())
|
||||
val updatedCurrencies = if (changed) currencies else currencies + blockchainNetwork
|
||||
saveCurrencies(cardId, updatedCurrencies.distinct())
|
||||
}
|
||||
|
||||
fun saveCardCurrencies(cardId: String, currencies: CardCurrencies) {
|
||||
saveTokens(cardId, currencies.tokens)
|
||||
saveBlockchains(cardId, currencies.blockchains)
|
||||
fun removeToken(cardId: String, token: Token, blockchainNetwork: BlockchainNetwork) {
|
||||
val currencies = loadSavedCurrencies(cardId).map {
|
||||
if (it == blockchainNetwork) {
|
||||
it.copy(tokens = it.tokens.filterNot { it == token })
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
saveCurrencies(cardId, currencies)
|
||||
}
|
||||
|
||||
fun saveAddedToken(cardId: String, token: Token) {
|
||||
val tokens = loadSavedTokens(cardId) + token
|
||||
saveTokens(cardId, tokens)
|
||||
}
|
||||
|
||||
fun saveAddedTokens(cardId: String, tokens: Collection<Token>) {
|
||||
saveTokens(cardId, loadSavedTokens(cardId) + tokens.distinct())
|
||||
}
|
||||
|
||||
fun saveAddedBlockchain(cardId: String, blockchain: Blockchain) {
|
||||
val blockchains = loadSavedBlockchains(cardId) + blockchain
|
||||
saveBlockchains(cardId, blockchains.distinct())
|
||||
}
|
||||
|
||||
fun removeToken(cardId: String, token: Token) {
|
||||
val tokens = loadSavedTokens(cardId).filterNot { it == token }
|
||||
saveTokens(cardId, tokens)
|
||||
}
|
||||
|
||||
fun removeBlockchain(cardId: String, blockchain: Blockchain) {
|
||||
val blockchains = loadSavedBlockchains(cardId).filterNot { it == blockchain }
|
||||
saveBlockchains(cardId, blockchains)
|
||||
fun removeBlockchain(cardId: String, blockchainNetwork: BlockchainNetwork) {
|
||||
val currencies = loadSavedCurrencies(cardId).filterNot { it == blockchainNetwork }
|
||||
saveCurrencies(cardId, currencies)
|
||||
}
|
||||
|
||||
fun removeCurrencies(cardId: String) {
|
||||
saveTokens(cardId, emptyList())
|
||||
saveBlockchains(cardId, emptyList())
|
||||
saveCurrencies(cardId, emptyList())
|
||||
}
|
||||
|
||||
private fun loadSavedTokens(cardId: String): List<Token> {
|
||||
@Deprecated("Use BlockchainNetwork instead")
|
||||
private fun loadSavedTokens(cardId: String): List<TokenDao> {
|
||||
val json = try {
|
||||
context.readFileText(getFileNameForTokens(cardId))
|
||||
} catch (exception: Exception) {
|
||||
|
|
@ -82,17 +82,13 @@ class CurrenciesRepository(val context: Application) {
|
|||
}
|
||||
|
||||
return try {
|
||||
tokensAdapter.fromJson(json)!!.map { it.toToken() }.distinct()
|
||||
tokensAdapter.fromJson(json) ?: emptyList()
|
||||
} catch (exception: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveTokens(cardId: String, tokens: List<Token>) {
|
||||
val json = tokensAdapter.toJson(tokens.distinct().map { TokenDao.fromToken(it) })
|
||||
context.rewriteFile(json, getFileNameForTokens(cardId))
|
||||
}
|
||||
|
||||
@Deprecated("Use BlockchainNetwork instead")
|
||||
private fun loadSavedBlockchains(cardId: String): List<Blockchain> {
|
||||
return try {
|
||||
val json = context.readFileText(getFileNameForBlockchains(cardId))
|
||||
|
|
@ -102,8 +98,61 @@ class CurrenciesRepository(val context: Application) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun saveBlockchains(cardId: String, blockchains: List<Blockchain>) {
|
||||
val json = blockchainsAdapter.toJson(blockchains.distinct())
|
||||
fun loadSavedCurrencies(
|
||||
cardId: String,
|
||||
derivationStyle: DerivationStyle? = null
|
||||
): List<BlockchainNetwork> {
|
||||
if (DemoHelper.isDemoCardId(cardId)) {
|
||||
return loadDemoCurrencies(cardId)
|
||||
}
|
||||
return try {
|
||||
val json = context.readFileText(getFileNameForBlockchains(cardId))
|
||||
blockchainNetworkAdapter.fromJson(json)?.distinct() ?: loadSavedCurrenciesOldWay(
|
||||
cardId,
|
||||
derivationStyle
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadDemoCurrencies(cardId: String): List<BlockchainNetwork> {
|
||||
return DemoHelper.config.demoBlockchains.map {
|
||||
BlockchainNetwork(
|
||||
blockchain = it,
|
||||
derivationPath = it.derivationPath(DerivationStyle.LEGACY)?.rawPath,
|
||||
tokens = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadSavedCurrenciesOldWay(
|
||||
cardId: String, derivationStyle: DerivationStyle?
|
||||
): List<BlockchainNetwork> {
|
||||
val blockchains = loadSavedBlockchains(cardId)
|
||||
val tokens = loadSavedTokens(cardId)
|
||||
val currencies = getSupportedTokens()
|
||||
val blockchainNetworks = blockchains.map { blockchain ->
|
||||
BlockchainNetwork(
|
||||
blockchain = blockchain,
|
||||
derivationPath = blockchain.derivationPath(derivationStyle)?.rawPath,
|
||||
tokens = tokens
|
||||
.filter { it.blockchainDao.toBlockchain() == blockchain }
|
||||
.map {
|
||||
val token = it.toToken()
|
||||
val id = currencies
|
||||
.find { it.contracts?.find { it.address == token.contractAddress } != null }
|
||||
?.id
|
||||
token.copy(id = id)
|
||||
}
|
||||
)
|
||||
}
|
||||
saveCurrencies(cardId, blockchainNetworks) // migrate saved currencies
|
||||
return blockchainNetworks
|
||||
}
|
||||
|
||||
fun saveCurrencies(cardId: String, currencies: List<BlockchainNetwork>) {
|
||||
val json = blockchainNetworkAdapter.toJson(currencies)
|
||||
context.rewriteFile(json, getFileNameForBlockchains(cardId))
|
||||
}
|
||||
|
||||
|
|
@ -116,18 +165,10 @@ class CurrenciesRepository(val context: Application) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getSupportedTokens(isTestNet: Boolean = false): List<Token> {
|
||||
val supportedTokens = if (isTestNet) {
|
||||
getSupportedTokens().mapNotNull { it.getTestnetVersion() }
|
||||
} else {
|
||||
getSupportedTokens()
|
||||
}
|
||||
|
||||
val blockchainTokens = supportedTokens.mapNotNull { blockchain ->
|
||||
loadTokensJson(blockchain)?.let { fromJsonToTokensDao(it, blockchain) }
|
||||
}
|
||||
val tokens = blockchainTokens.flatten().map { it.toToken() }
|
||||
return tokens
|
||||
fun getSupportedTokens(isTestNet: Boolean = false): List<Currency> {
|
||||
val fileName = if (isTestNet) "testnet_tokens" else "tokens"
|
||||
val json = context.assets.readJsonFileToString(fileName)
|
||||
return currenciesAdapter.fromJson(json)!!.tokens.map { Currency.fromJsonObject(it) }
|
||||
}
|
||||
|
||||
private fun loadTokensJson(blockchain: Blockchain): String? {
|
||||
|
|
@ -142,7 +183,7 @@ class CurrenciesRepository(val context: Application) {
|
|||
|
||||
private fun getFileName(blockchain: Blockchain): String {
|
||||
return StringBuilder().apply {
|
||||
append(blockchain.id.toLowerCase().replace("/test", ""))
|
||||
append(blockchain.id.lowercase(Locale.getDefault()).replace("/test", ""))
|
||||
append("_tokens")
|
||||
appendIf("_testnet") { blockchain.isTestnet() }
|
||||
}.toString()
|
||||
|
|
@ -152,17 +193,6 @@ class CurrenciesRepository(val context: Application) {
|
|||
return obsoleteTokensAdapter.fromJson(tokenJson)!!.map { it.toTokenDao(blockchain) }
|
||||
}
|
||||
|
||||
private fun getSupportedTokens(): List<Blockchain> {
|
||||
return listOf(
|
||||
Blockchain.Ethereum,
|
||||
Blockchain.BSC,
|
||||
Blockchain.Binance,
|
||||
Blockchain.Polygon,
|
||||
Blockchain.Avalanche,
|
||||
Blockchain.Fantom,
|
||||
)
|
||||
}
|
||||
|
||||
fun getBlockchains(
|
||||
cardFirmware: FirmwareVersion,
|
||||
isTestNet: Boolean = false
|
||||
|
|
@ -178,9 +208,11 @@ class CurrenciesRepository(val context: Application) {
|
|||
// Use this list to temporarily exclude a blockchain from the list of tokens.
|
||||
private fun excludeUnsupportedBlockchains(blockchains: List<Blockchain>): List<Blockchain> {
|
||||
return blockchains.toMutableList().apply {
|
||||
removeAll(listOf(
|
||||
removeAll(
|
||||
listOf(
|
||||
// Any blockchain
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,24 +250,11 @@ data class TokenDao(
|
|||
symbol = symbol,
|
||||
contractAddress = contractAddress,
|
||||
decimals = decimalCount,
|
||||
blockchain = blockchainDao.toBlockchain()
|
||||
).apply {
|
||||
customIconUrl?.let { this.setCustomIconUrl(it) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromToken(token: Token): TokenDao {
|
||||
return TokenDao(
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
contractAddress = token.contractAddress,
|
||||
decimalCount = token.decimals,
|
||||
blockchainDao = BlockchainDao.fromBlockchain(token.blockchain),
|
||||
customIconUrl = token.getCustomIconUrl()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
@ -280,29 +299,78 @@ data class ObsoleteTokenDao(
|
|||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CardCurrenciesDao(
|
||||
val tokens: List<TokenDao>,
|
||||
val blockchains: List<Blockchain>,
|
||||
) {
|
||||
fun toCardCurrencies(): CardCurrencies {
|
||||
return CardCurrencies(
|
||||
tokens = tokens.map { it.toToken() }.distinct(),
|
||||
blockchains = blockchains
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromCardCurrencies(cardCurrencies: CardCurrencies): CardCurrenciesDao {
|
||||
return CardCurrenciesDao(
|
||||
tokens = cardCurrencies.tokens.map { TokenDao.fromToken(it) }.distinct(),
|
||||
blockchains = cardCurrencies.blockchains
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
//@JsonClass(generateAdapter = true)
|
||||
//data class CardCurrenciesDao(
|
||||
// val tokens: List<TokenDao>,
|
||||
// val blockchains: List<Blockchain>,
|
||||
//) {
|
||||
// fun toCardCurrencies(): CardCurrencies {
|
||||
// return CardCurrencies(
|
||||
// tokens = tokens.map { it.toToken() }.distinct(),
|
||||
// blockchains = blockchains
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// companion object {
|
||||
// fun fromCardCurrencies(cardCurrencies: CardCurrencies): CardCurrenciesDao {
|
||||
// return CardCurrenciesDao(
|
||||
// tokens = cardCurrencies.tokens.map { TokenDao.fromToken(it) }.distinct(),
|
||||
// blockchains = cardCurrencies.blockchains
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
data class CardCurrencies(
|
||||
val tokens: List<Token>,
|
||||
val blockchains: List<Blockchain>,
|
||||
)
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class BlockchainNetwork(
|
||||
val blockchain: Blockchain,
|
||||
val derivationPath: String?,
|
||||
val tokens: List<Token>
|
||||
) {
|
||||
|
||||
constructor(blockchain: Blockchain, card: Card) : this(
|
||||
blockchain = blockchain,
|
||||
derivationPath = if (card.settings.isHDWalletAllowed) blockchain.derivationPath(card.derivationStyle)?.rawPath else null,
|
||||
tokens = emptyList()
|
||||
)
|
||||
|
||||
|
||||
fun updateTokens(tokens: List<Token>): BlockchainNetwork {
|
||||
return copy(
|
||||
tokens = (this.tokens + tokens).distinct()
|
||||
)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as BlockchainNetwork
|
||||
|
||||
if (blockchain != other.blockchain) return false
|
||||
if (derivationPath != other.derivationPath) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = blockchain.hashCode()
|
||||
result = 31 * result + (derivationPath?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromWalletManager(walletManager: WalletManager): BlockchainNetwork {
|
||||
return BlockchainNetwork(
|
||||
walletManager.wallet.blockchain,
|
||||
walletManager.wallet.publicKey.derivationPath?.rawPath,
|
||||
walletManager.cardTokens.toList()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
135
app/src/main/java/com/tangem/tap/domain/tokens/Currency.kt
Normal file
135
app/src/main/java/com/tangem/tap/domain/tokens/Currency.kt
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package com.tangem.tap.domain.tokens
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CurrencyFromJson(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val contracts: List<ContractFromJson>? = null
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ContractFromJson(
|
||||
val networkId: String,
|
||||
val address: String,
|
||||
val decimalCount: Int
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CurrenciesFromJson(
|
||||
val imageHost: String,
|
||||
val tokens: List<CurrencyFromJson>
|
||||
)
|
||||
|
||||
|
||||
fun List<ContractFromJson>.toContracts(): List<Contract> {
|
||||
return mapNotNull { Contract.fromJsonObject(it) }
|
||||
}
|
||||
|
||||
data class Currency(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val iconUrl: String,
|
||||
val contracts: List<Contract>?
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun fromJsonObject(currency: CurrencyFromJson): Currency {
|
||||
return Currency(
|
||||
id = currency.id,
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
iconUrl = getIconUrl(currency.id),
|
||||
contracts = currency.contracts?.toContracts()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Contract(
|
||||
val networkId: String,
|
||||
val blockchain: Blockchain,
|
||||
val address: String,
|
||||
val decimalCount: Int,
|
||||
val iconUrl: String,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun fromJsonObject(contract: ContractFromJson): Contract? {
|
||||
val blockchain = Blockchain.fromNetworkId(contract.networkId) ?: return null
|
||||
return Contract(
|
||||
networkId = contract.networkId,
|
||||
blockchain = blockchain,
|
||||
address = contract.address,
|
||||
decimalCount = contract.decimalCount,
|
||||
iconUrl = getIconUrl(contract.networkId)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getIconUrl(id: String): String {
|
||||
return "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/$id.png"
|
||||
}
|
||||
|
||||
|
||||
fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
|
||||
return when (networkId) {
|
||||
"avalanche" -> Blockchain.Avalanche
|
||||
"binancecoin" -> Blockchain.Binance
|
||||
"binance-smart-chain" -> Blockchain.BSC
|
||||
"ethereum" -> Blockchain.Ethereum
|
||||
"polygon-pos" -> Blockchain.Polygon
|
||||
"solana" -> Blockchain.Solana
|
||||
"fantom" -> Blockchain.Fantom
|
||||
"bitcoin" -> Blockchain.Bitcoin
|
||||
"bitcoin-cash" -> Blockchain.BitcoinCash
|
||||
"cardano" -> Blockchain.CardanoShelley
|
||||
"dogecoin" -> Blockchain.Dogecoin
|
||||
"ducatus" -> Blockchain.Ducatus
|
||||
"litecoin" -> Blockchain.Litecoin
|
||||
"rsk" -> Blockchain.RSK
|
||||
"stellar" -> Blockchain.Stellar
|
||||
"tezos" -> Blockchain.Tezos
|
||||
"ripple" -> Blockchain.XRP
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun Blockchain.toNetworkId(): String {
|
||||
return when (this) {
|
||||
Blockchain.Unknown -> "unknown"
|
||||
Blockchain.Avalanche -> "avalanche"
|
||||
Blockchain.AvalancheTestnet -> "avalaunche"
|
||||
Blockchain.Binance -> "binancecoin"
|
||||
Blockchain.BinanceTestnet -> "binancecoin"
|
||||
Blockchain.BSC -> "binance-smart-chain"
|
||||
Blockchain.BSCTestnet -> "binance-smart-chain"
|
||||
Blockchain.Bitcoin -> "bitcoin"
|
||||
Blockchain.BitcoinTestnet -> "bitcoin"
|
||||
Blockchain.BitcoinCash -> "bitcoin-cash"
|
||||
Blockchain.BitcoinCashTestnet -> "bitcoin-cash"
|
||||
Blockchain.Cardano -> "cardano"
|
||||
Blockchain.CardanoShelley -> "cardano"
|
||||
Blockchain.Dogecoin -> "dogecoin"
|
||||
Blockchain.Ducatus -> "ducatus"
|
||||
Blockchain.Ethereum -> "ethereum"
|
||||
Blockchain.EthereumTestnet -> "ethereum"
|
||||
Blockchain.Fantom -> "fantom"
|
||||
Blockchain.FantomTestnet -> "fantom"
|
||||
Blockchain.Litecoin -> "litecoin"
|
||||
Blockchain.Polygon -> "matic-network"
|
||||
Blockchain.PolygonTestnet -> "matic-networks"
|
||||
Blockchain.RSK -> "rootstock"
|
||||
Blockchain.Stellar -> "stellar"
|
||||
Blockchain.StellarTestnet -> "stellar"
|
||||
Blockchain.Solana -> "solana"
|
||||
Blockchain.SolanaTestnet -> "solana"
|
||||
Blockchain.Tezos -> "tezos"
|
||||
Blockchain.XRP -> "ripple"
|
||||
}
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ internal class DemoOnboardingNoteMiddleware : DemoMiddleware {
|
|||
val balanceAmount = config.getBalance(walletManager.wallet.blockchain)
|
||||
val loadedBalance = noteState.walletBalance.copy(
|
||||
value = balanceAmount.value!!,
|
||||
currency = Currency.Blockchain(walletManager.wallet.blockchain),
|
||||
currency = Currency.Blockchain(walletManager.wallet.blockchain, null),
|
||||
state = ProgressState.Done,
|
||||
error = null,
|
||||
criticalError = null
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
package com.tangem.tap.features.details.redux.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationParams
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.getFromClipboard
|
||||
|
|
@ -14,10 +16,14 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.currenciesRepository
|
||||
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
|
||||
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.tokens.BlockchainNetwork
|
||||
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.demo.DemoHelper
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -164,14 +170,16 @@ class WalletConnectMiddleware {
|
|||
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
|
||||
)
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -229,22 +237,35 @@ class WalletConnectMiddleware {
|
|||
}
|
||||
|
||||
return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) {
|
||||
store.state.walletState.getWalletManager(blockchainToMake)
|
||||
?: factory.makeWalletManagerForApp(scanResponse, blockchainToMake)
|
||||
val derivationPath = blockchainToMake.derivationPath(card.derivationStyle)?.rawPath
|
||||
store.state.walletState.getWalletManager(
|
||||
Currency.Blockchain(blockchainToMake, derivationPath)
|
||||
)
|
||||
?: factory.makeWalletManagerForApp(
|
||||
scanResponse,
|
||||
blockchainToMake,
|
||||
card.derivationStyle?.let { DerivationParams.Default(it) }
|
||||
)
|
||||
?.also { walletManager ->
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain))
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.AddBlockchain(
|
||||
BlockchainNetwork.fromWalletManager(walletManager), walletManager
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val walletManager = factory.makeWalletManagerForApp(scanResponse, blockchainToMake)
|
||||
if (currenciesRepository.loadCardCurrencies(card.cardId)?.blockchains?.contains(
|
||||
blockchainToMake
|
||||
) != true
|
||||
val walletManager = factory.makeWalletManagerForApp(
|
||||
scanResponse,
|
||||
blockchainToMake,
|
||||
card.derivationStyle?.let { DerivationParams.Default(it) }
|
||||
)
|
||||
if (currenciesRepository.loadSavedCurrencies(card.cardId, card.derivationStyle)
|
||||
.find { it.blockchain == blockchainToMake } != null
|
||||
) {
|
||||
walletManager?.let {
|
||||
currenciesRepository.saveAddedBlockchain(
|
||||
currenciesRepository.saveUpdatedCurrency(
|
||||
card.cardId,
|
||||
blockchainToMake
|
||||
BlockchainNetwork.fromWalletManager(walletManager)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ class AdditionalEmailInfo {
|
|||
var address: String = "",
|
||||
var explorerLink: String = "",
|
||||
var host: String = "",
|
||||
var derivationPath: String = "",
|
||||
)
|
||||
|
||||
var appVersion: String = ""
|
||||
|
|
@ -159,7 +160,8 @@ class AdditionalEmailInfo {
|
|||
blockchain = manager.wallet.blockchain,
|
||||
address = getAddress(manager.wallet),
|
||||
explorerLink = getExploreUri(manager.wallet),
|
||||
host = manager.currentHost
|
||||
host = manager.currentHost,
|
||||
derivationPath = manager.wallet.publicKey.derivationPath?.rawPath ?: ""
|
||||
)
|
||||
)
|
||||
if (manager.cardTokens.isNotEmpty()) {
|
||||
|
|
@ -173,7 +175,8 @@ class AdditionalEmailInfo {
|
|||
blockchain = wallet.blockchain,
|
||||
address = getAddress(wallet),
|
||||
explorerLink = getExploreUri(wallet),
|
||||
host = host
|
||||
host = host,
|
||||
derivationPath = wallet.publicKey.derivationPath?.rawPath ?: ""
|
||||
)
|
||||
|
||||
this.destinationAddress = destinationAddress
|
||||
|
|
@ -325,6 +328,7 @@ class EmailDataBuilder(
|
|||
builder.appendKeyValue("Blockchain", it.blockchain.fullName)
|
||||
builder.appendKeyValue("Host", it.host)
|
||||
builder.appendKeyValue("Wallet address", it.address)
|
||||
builder.appendKeyValue("Derivation path", it.derivationPath)
|
||||
builder.appendKeyValue("Explorer link", it.explorerLink)
|
||||
|
||||
infoHolder.tokens[it.blockchain]?.let { tokens ->
|
||||
|
|
@ -332,6 +336,7 @@ class EmailDataBuilder(
|
|||
appendLine()
|
||||
tokens.forEach { token ->
|
||||
builder.appendKeyValue("Name", token.name)
|
||||
if (token.id != null) builder.appendKeyValue("Id", token.id ?: "")
|
||||
builder.appendKeyValue("Contract address", token.contractAddress)
|
||||
}
|
||||
}
|
||||
|
|
@ -342,6 +347,7 @@ class EmailDataBuilder(
|
|||
fun appendTxFailedBlockchainInfo(error: String): EmailDataBuilder {
|
||||
val walletInfo = infoHolder.onSendErrorWalletInfo ?: AdditionalEmailInfo.EmailWalletInfo()
|
||||
builder.appendKeyValue("Blockchain", walletInfo.blockchain.fullName)
|
||||
builder.appendKeyValue("Derivation path", walletInfo.derivationPath)
|
||||
builder.appendKeyValue("Host", walletInfo.host)
|
||||
builder.appendKeyValue("Token", infoHolder.token)
|
||||
builder.appendKeyValue("Error", error)
|
||||
|
|
|
|||
|
|
@ -7,10 +7,13 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.accompanist.appcompattheme.AppCompatTheme
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.home.compose.StoriesScreen
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.home.redux.HomeState
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||
import com.tangem.tap.features.tokens.redux.TokensAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
|
@ -35,7 +38,12 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber<HomeState
|
|||
StoriesScreen(
|
||||
homeState,
|
||||
onScanButtonClick = { store.dispatch(HomeAction.ReadCard) },
|
||||
onShopButtonClick = { store.dispatch(HomeAction.GoToShop(getRegionProvider())) }
|
||||
onShopButtonClick = { store.dispatch(HomeAction.GoToShop(getRegionProvider())) },
|
||||
onSearchTokensClick = {
|
||||
store.dispatch(TokensAction.AllowToAddTokens(false))
|
||||
store.dispatch(TokensAction.LoadCurrencies())
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import androidx.compose.foundation.Image
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -16,9 +19,13 @@ import androidx.compose.ui.graphics.ColorFilter
|
|||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.SpacerS24
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.tap.features.home.redux.HomeState
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -28,7 +35,8 @@ import kotlin.math.max
|
|||
fun StoriesScreen(
|
||||
homeState: MutableState<HomeState> = mutableStateOf(HomeState()),
|
||||
onScanButtonClick: () -> Unit,
|
||||
onShopButtonClick: () -> Unit
|
||||
onShopButtonClick: () -> Unit,
|
||||
onSearchTokensClick: () -> Unit,
|
||||
) {
|
||||
val steps = 6
|
||||
val stepDuration = 8_000
|
||||
|
|
@ -78,7 +86,8 @@ fun StoriesScreen(
|
|||
}
|
||||
)
|
||||
Box(
|
||||
Modifier.weight(1f)
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.fillMaxHeight()
|
||||
.pointerInput(isPressed) {
|
||||
detectTapGestures(
|
||||
|
|
@ -107,7 +116,7 @@ fun StoriesScreen(
|
|||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
SpacerS24()
|
||||
StoriesProgressBar(
|
||||
|
|
@ -138,15 +147,44 @@ fun StoriesScreen(
|
|||
6 -> StoriesWalletForEveryone()
|
||||
}
|
||||
}
|
||||
HomeButtons(
|
||||
isDarkBackground = isDarkBackground,
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(16.dp, 0.dp, 16.dp, 37.dp)
|
||||
.fillMaxWidth(),
|
||||
onScanButtonClick = onScanButtonClick,
|
||||
onShopButtonClick = onShopButtonClick
|
||||
)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
if (currentStep.value == 4) Button(
|
||||
onClick = onSearchTokensClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.height(48.dp)
|
||||
,
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
backgroundColor = Color.White,
|
||||
contentColor = Color(0xFF080C10)
|
||||
)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_search),
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.search_tokens_title),
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 16.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
HomeButtons(
|
||||
isDarkBackground = isDarkBackground,
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 0.dp, end = 16.dp, bottom = 37.dp)
|
||||
.fillMaxWidth(),
|
||||
onScanButtonClick = onScanButtonClick,
|
||||
onShopButtonClick = onShopButtonClick
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -154,5 +192,5 @@ fun StoriesScreen(
|
|||
@Preview
|
||||
@Composable
|
||||
fun InstagramScreenPreview() {
|
||||
StoriesScreen(onScanButtonClick = {}, onShopButtonClick = {})
|
||||
StoriesScreen(onScanButtonClick = {}, onShopButtonClick = {}, onSearchTokensClick = {})
|
||||
}
|
||||
|
|
@ -67,7 +67,9 @@ class OnboardingManager(
|
|||
}
|
||||
}
|
||||
|
||||
return balance.copy(currency = Currency.Blockchain(walletManager.wallet.blockchain))
|
||||
return balance.copy(currency = Currency.Blockchain(
|
||||
walletManager.wallet.blockchain, walletManager.wallet.publicKey.derivationPath?.rawPath)
|
||||
)
|
||||
}
|
||||
|
||||
fun activationStarted(cardId: String) {
|
||||
|
|
@ -81,7 +83,7 @@ class OnboardingManager(
|
|||
|
||||
data class OnboardingWalletBalance(
|
||||
val value: BigDecimal = BigDecimal.ZERO,
|
||||
val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown),
|
||||
val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown, null),
|
||||
val hasIncomingTransaction: Boolean = false,
|
||||
val state: ProgressState,
|
||||
val error: TapError? = null,
|
||||
|
|
|
|||
|
|
@ -113,7 +113,10 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
|
|||
|
||||
val isLoadedBefore = noteState.walletBalance.state != ProgressState.Loading
|
||||
val balanceIsLoading = noteState.walletBalance.copy(
|
||||
currency = Currency.Blockchain(walletManager.wallet.blockchain),
|
||||
currency = Currency.Blockchain(
|
||||
walletManager.wallet.blockchain,
|
||||
walletManager.wallet.publicKey.derivationPath?.rawPath
|
||||
),
|
||||
state = ProgressState.Loading,
|
||||
error = null,
|
||||
criticalError = null
|
||||
|
|
|
|||
|
|
@ -215,7 +215,10 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
}
|
||||
val isLoadedBefore = twinCardsState.walletBalance.state != ProgressState.Loading
|
||||
val balanceIsLoading = twinCardsState.walletBalance.copy(
|
||||
currency = Currency.Blockchain(walletManager.wallet.blockchain),
|
||||
currency = Currency.Blockchain(
|
||||
walletManager.wallet.blockchain,
|
||||
walletManager.wallet.publicKey.derivationPath?.rawPath
|
||||
),
|
||||
state = ProgressState.Loading,
|
||||
error = null,
|
||||
criticalError = null
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import com.tangem.tap.domain.TangemSigner
|
|||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.extensions.minimalAmount
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.demo.DemoTransactionSender
|
||||
import com.tangem.tap.features.demo.isDemoWallet
|
||||
import com.tangem.tap.features.send.redux.*
|
||||
|
|
@ -203,11 +204,11 @@ private fun sendTransaction(
|
|||
}
|
||||
scope.launch(Dispatchers.IO) {
|
||||
withContext(Dispatchers.Main) {
|
||||
dispatch(WalletAction.LoadWallet(walletManager.wallet.blockchain))
|
||||
dispatch(WalletAction.LoadWallet(BlockchainNetwork.fromWalletManager(walletManager)))
|
||||
}
|
||||
delay(11000) // more than 10000 to avoid throttling
|
||||
withContext(Dispatchers.Main) {
|
||||
dispatch(WalletAction.LoadWallet(walletManager.wallet.blockchain))
|
||||
dispatch(WalletAction.LoadWallet(BlockchainNetwork.fromWalletManager(walletManager)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
package com.tangem.tap.features.tokens.redux
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.wallet.R
|
||||
|
||||
sealed class CurrencyListItem {
|
||||
var isAdded: Boolean = false
|
||||
var isLocked: Boolean = false
|
||||
|
||||
data class TokenListItem(val token: Token) : CurrencyListItem()
|
||||
data class BlockchainListItem(val blockchain: Blockchain) : CurrencyListItem()
|
||||
|
||||
data class TitleListItem(
|
||||
@StringRes val titleResId: Int,
|
||||
var isContentShown: Boolean = true,
|
||||
val blockchain: Blockchain? = null,
|
||||
) : CurrencyListItem()
|
||||
|
||||
companion object {
|
||||
fun createListOfCurrencies(
|
||||
blockchains: List<Blockchain>,
|
||||
tokens: List<Token>,
|
||||
): List<CurrencyListItem> {
|
||||
val blockchainsItemList = createBlockchainList(blockchains)
|
||||
val tokenItemsLists = tokens.groupBy { it.blockchain }.map {
|
||||
createTokensList(R.string.add_tokens_subtitle_format, it.key, it.value)
|
||||
}
|
||||
return blockchainsItemList + tokenItemsLists.flatten()
|
||||
}
|
||||
|
||||
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.isTheSameOrTestnetBlockchain(blockchain)
|
||||
}
|
||||
}
|
||||
|
||||
fun List<CurrencyListItem>.addTokensForBlockchain(
|
||||
blockchain: Blockchain, fullCurrenciesList: List<CurrencyListItem>,
|
||||
): List<CurrencyListItem> {
|
||||
val tokensToAdd = fullCurrenciesList.filter {
|
||||
it is CurrencyListItem.TokenListItem && it.isTheSameOrTestnetBlockchain(blockchain)
|
||||
}
|
||||
val indexToInsert = indexOfFirst {
|
||||
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.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()
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.features.tokens.redux
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.tap.domain.tokens.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import org.rekotlin.Action
|
||||
|
||||
|
|
@ -8,14 +10,19 @@ sealed class TokensAction : Action {
|
|||
|
||||
object ResetState : TokensAction()
|
||||
|
||||
object LoadCurrencies : TokensAction() {
|
||||
data class Success(val currencies: List<CurrencyListItem>) : TokensAction()
|
||||
data class AllowToAddTokens(val allow: Boolean) : TokensAction()
|
||||
|
||||
data class LoadCurrencies(val supportedBlockchains: List<Blockchain>? = null) : TokensAction() {
|
||||
data class Success(val currencies: List<Currency>) : TokensAction()
|
||||
}
|
||||
|
||||
data class SetAddedCurrencies(val wallets: List<WalletData>) : TokensAction()
|
||||
data class SetAddedCurrencies(
|
||||
val wallets: List<WalletData>, val derivationStyle: DerivationStyle?
|
||||
) : TokensAction()
|
||||
data class SetNonRemovableCurrencies(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()
|
||||
data class SaveChanges(
|
||||
val addedTokens: List<TokenWithBlockchain>,
|
||||
val addedBlockchains: List<Blockchain>
|
||||
) : TokensAction()
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.tokens.redux
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationParams
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
|
|
@ -18,6 +19,13 @@ import com.tangem.tap.common.redux.AppState
|
|||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
|
||||
import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -29,7 +37,7 @@ class TokensMiddleware {
|
|||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is TokensAction.LoadCurrencies -> handleLoadCurrencies()
|
||||
is TokensAction.LoadCurrencies -> handleLoadCurrencies(action)
|
||||
is TokensAction.SaveChanges -> handleSaveChanges(action)
|
||||
}
|
||||
next(action)
|
||||
|
|
@ -37,35 +45,41 @@ class TokensMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleLoadCurrencies() {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
val isTestcard = scanResponse.card.isTestCard
|
||||
private fun handleLoadCurrencies(action: TokensAction.LoadCurrencies) {
|
||||
val scanResponse = store.state.globalState.scanResponse
|
||||
val isTestcard = scanResponse?.card?.isTestCard ?: false
|
||||
|
||||
val currencies = currenciesRepository.getSupportedTokens(isTestcard)
|
||||
.filter(action.supportedBlockchains?.toSet())
|
||||
|
||||
val tokens = currenciesRepository.getSupportedTokens(isTestcard)
|
||||
val blockchains = currenciesRepository.getBlockchains(
|
||||
cardFirmware = scanResponse.card.firmwareVersion,
|
||||
isTestNet = isTestcard
|
||||
).sortedBy { it.fullName }
|
||||
val currencies = CurrencyListItem.createListOfCurrencies(blockchains, tokens)
|
||||
store.dispatch(TokensAction.LoadCurrencies.Success(currencies))
|
||||
}
|
||||
|
||||
private fun handleSaveChanges(action: TokensAction.SaveChanges) {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
|
||||
val candidatesToAdd = action.addedItems
|
||||
if (candidatesToAdd.isEmpty()) return
|
||||
val currentTokens = store.state.tokensState.addedWallets.toTokens()
|
||||
val currentBlockchains = store.state.tokensState.addedWallets.toBlockchains(
|
||||
store.state.tokensState.derivationStyle
|
||||
)
|
||||
|
||||
val blockchains = candidatesToAdd.filterIsInstance<CurrencyListItem.BlockchainListItem>()
|
||||
.map { it.blockchain }
|
||||
val tokens = candidatesToAdd.filterIsInstance<CurrencyListItem.TokenListItem>()
|
||||
.map { it.token }
|
||||
if (blockchains.isEmpty() && tokens.isEmpty()) return
|
||||
val blockchainsToAdd = action.addedBlockchains.filter { !currentBlockchains.contains(it) }
|
||||
val blockchainsToRemove = currentBlockchains.filter { !action.addedBlockchains.contains(it) }
|
||||
|
||||
val tokensToAdd = action.addedTokens.filter { !currentTokens.contains(it.token) }
|
||||
val tokensToRemove = currentTokens.filter { token -> !action.addedTokens.any { it.token == token } }
|
||||
|
||||
removeCurrenciesIfNeeded(blockchainsToRemove, tokensToRemove)
|
||||
|
||||
if (tokensToAdd.isEmpty() && blockchainsToAdd.isEmpty()) {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
return
|
||||
}
|
||||
|
||||
if (scanResponse.supportsHdWallet()) {
|
||||
deriveMissingBlockchains(scanResponse, blockchains, tokens)
|
||||
deriveMissingBlockchains(scanResponse, blockchainsToAdd, tokensToAdd)
|
||||
} else {
|
||||
submitAdd(blockchains, tokens)
|
||||
submitAdd(blockchainsToAdd, tokensToAdd, scanResponse)
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
}
|
||||
|
|
@ -73,7 +87,7 @@ class TokensMiddleware {
|
|||
private fun deriveMissingBlockchains(
|
||||
scanResponse: ScanResponse,
|
||||
blockchains: List<Blockchain>,
|
||||
tokens: List<Token>
|
||||
tokens: List<TokenWithBlockchain>
|
||||
) {
|
||||
val derivationDataList = listOfNotNull(
|
||||
getDerivations(EllipticCurve.Secp256k1, scanResponse, blockchains, tokens),
|
||||
|
|
@ -104,7 +118,7 @@ class TokensMiddleware {
|
|||
derivedKeys = updatedDerivedKeys
|
||||
)
|
||||
store.dispatchOnMain(GlobalAction.SaveScanNoteResponse(updatedScanResponse))
|
||||
submitAdd(blockchains, tokens)
|
||||
submitAdd(blockchains, tokens, scanResponse)
|
||||
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
|
|
@ -120,13 +134,12 @@ class TokensMiddleware {
|
|||
curve: EllipticCurve,
|
||||
scanResponse: ScanResponse,
|
||||
blockchains: List<Blockchain>,
|
||||
tokens: List<Token>
|
||||
tokens: List<TokenWithBlockchain>
|
||||
): DerivationData? {
|
||||
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
|
||||
|
||||
val tokenBlockchains = tokens.map { it.blockchain }
|
||||
val derivationPathsCandidates = (blockchains + tokenBlockchains).distinct()
|
||||
.mapNotNull { it.derivationPath() }
|
||||
val derivationPathsCandidates = (blockchains + tokens.map { it.blockchain }).distinct()
|
||||
.mapNotNull { it.derivationPath(scanResponse.card.derivationStyle) }
|
||||
|
||||
val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey()
|
||||
val alreadyDerivedKeys: ExtendedPublicKeysMap =
|
||||
|
|
@ -147,11 +160,38 @@ class TokensMiddleware {
|
|||
val mapKeyOfWalletPublicKey: ByteArrayKey
|
||||
)
|
||||
|
||||
private fun submitAdd(blockchains: List<Blockchain>, tokens: List<Token>) {
|
||||
(blockchains.map {
|
||||
WalletAction.MultiWallet.AddBlockchain(it)
|
||||
private fun submitAdd(
|
||||
blockchains: List<Blockchain>, tokens: List<TokenWithBlockchain>, scanResponse: ScanResponse,
|
||||
) {
|
||||
val factory = store.state.globalState.tapWalletManager.walletManagerFactory
|
||||
|
||||
(blockchains.mapNotNull {
|
||||
val walletManager = factory.makeWalletManagerForApp(
|
||||
scanResponse, it,
|
||||
scanResponse.card.derivationStyle?.let { DerivationParams.Default(it) }
|
||||
) ?: return@mapNotNull null
|
||||
WalletAction.MultiWallet.AddBlockchain(BlockchainNetwork.fromWalletManager(walletManager), walletManager)
|
||||
} + tokens.map {
|
||||
WalletAction.MultiWallet.AddToken(it)
|
||||
val blockchainNetwork = BlockchainNetwork(it.blockchain, scanResponse.card)
|
||||
WalletAction.MultiWallet.AddToken(it.token, blockchainNetwork)
|
||||
}).forEach { store.dispatchOnMain(it) }
|
||||
}
|
||||
|
||||
private fun removeCurrenciesIfNeeded(blockchains: List<Blockchain>, tokens: List<Token>) {
|
||||
if (tokens.isNotEmpty()) {
|
||||
tokens.forEach { token ->
|
||||
store.state.walletState.getWalletData(token)?.let {
|
||||
store.dispatch(WalletAction.MultiWallet.RemoveWallet(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (blockchains.isNotEmpty()) {
|
||||
blockchains.forEach { blockchain ->
|
||||
store.state.walletState.getWalletData(blockchain)?.let {
|
||||
store.dispatch(WalletAction.MultiWallet.RemoveWallet(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
package com.tangem.tap.features.tokens.redux
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.tokens.CardCurrencies
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import org.rekotlin.Action
|
||||
|
||||
class TokensReducer {
|
||||
|
|
@ -19,35 +16,27 @@ private fun internalReduce(action: Action, state: AppState): TokensState {
|
|||
return when (action) {
|
||||
is TokensAction.ResetState -> TokensState()
|
||||
is TokensAction.LoadCurrencies.Success -> {
|
||||
tokensState.copy(currencies = action.currencies, shownCurrencies = action.currencies)
|
||||
tokensState.copy(currencies = action.currencies)
|
||||
}
|
||||
is TokensAction.SetAddedCurrencies -> {
|
||||
tokensState.copy(addedCurrencies = action.wallets.toCardCurrencies())
|
||||
|
||||
tokensState.copy(
|
||||
addedBlockchains = action.wallets.toBlockchains(action.derivationStyle),
|
||||
addedTokens = action.wallets.toTokensWithBlockchains(action.derivationStyle),
|
||||
addedWallets = action.wallets,
|
||||
derivationStyle = action.derivationStyle
|
||||
)
|
||||
}
|
||||
is TokensAction.ToggleShowTokensForBlockchain -> {
|
||||
if (action.isShown) {
|
||||
val shownCurrencies = tokensState.shownCurrencies
|
||||
.removeTokensForBlockchain(action.blockchain)
|
||||
shownCurrencies.toggleHeaderContentShownValue(action.blockchain)
|
||||
tokensState.copy(shownCurrencies = shownCurrencies)
|
||||
} else {
|
||||
val shownCurrencies = tokensState.shownCurrencies
|
||||
.addTokensForBlockchain(action.blockchain, tokensState.currencies)
|
||||
shownCurrencies.toggleHeaderContentShownValue(action.blockchain)
|
||||
tokensState.copy(shownCurrencies = shownCurrencies)
|
||||
}
|
||||
is TokensAction.SetNonRemovableCurrencies -> {
|
||||
tokensState.copy(
|
||||
nonRemovableBlockchains = action.wallets.toBlockchains(tokensState.derivationStyle),
|
||||
nonRemovableTokens = action.wallets.toTokensContractAddresses(),
|
||||
)
|
||||
}
|
||||
is TokensAction.OpenAllTokens -> {
|
||||
val shownCurrencies = tokensState.currencies
|
||||
shownCurrencies.forEach { if (it is CurrencyListItem.TitleListItem) it.isContentShown = true}
|
||||
tokensState.copy(shownCurrencies = tokensState.currencies)
|
||||
|
||||
is TokensAction.AllowToAddTokens -> {
|
||||
tokensState.copy(allowToAdd = action.allow)
|
||||
}
|
||||
else -> tokensState
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<WalletData>.toCardCurrencies(): CardCurrencies {
|
||||
val tokens = mapNotNull { (it.currency as? Currency.Token)?.token }.distinct()
|
||||
val blockchains = mapNotNull { (it.currency as? Currency.Blockchain)?.blockchain }.distinct()
|
||||
return CardCurrencies(tokens = tokens, blockchains = blockchains)
|
||||
}
|
||||
|
|
@ -1,18 +1,66 @@
|
|||
package com.tangem.tap.features.tokens.redux
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.tap.domain.tokens.CardCurrencies
|
||||
import com.tangem.tap.domain.tokens.Currency
|
||||
import com.tangem.tap.domain.tokens.fromNetworkId
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import org.rekotlin.StateType
|
||||
|
||||
data class TokensState(
|
||||
val addedTokens: LinkedHashSet<TokenWithAmount> = LinkedHashSet(),
|
||||
val addedCurrencies: CardCurrencies? = null,
|
||||
val currencies: List<CurrencyListItem> = emptyList(),
|
||||
val shownCurrencies: List<CurrencyListItem> = emptyList(),
|
||||
val addedWallets: List<WalletData> = emptyList(),
|
||||
val addedTokens: List<TokenWithBlockchain> = emptyList(),
|
||||
val addedBlockchains: List<Blockchain> = emptyList(),
|
||||
val nonRemovableTokens: List<ContractAddress> = emptyList(),
|
||||
val nonRemovableBlockchains: List<Blockchain> = emptyList(),
|
||||
val currencies: List<Currency> = emptyList(),
|
||||
val allowToAdd: Boolean = true,
|
||||
val derivationStyle: DerivationStyle? = null
|
||||
) : StateType
|
||||
|
||||
data class TokenWithAmount(
|
||||
val token: Token,
|
||||
val amount: Amount? = null
|
||||
typealias ContractAddress = String
|
||||
|
||||
fun List<WalletData>.toTokensContractAddresses(): List<ContractAddress> {
|
||||
return mapNotNull { (it.currency as? com.tangem.tap.features.wallet.redux.Currency.Token)?.token?.contractAddress }.distinct()
|
||||
}
|
||||
|
||||
fun List<WalletData>.toTokens(): List<Token> {
|
||||
return mapNotNull { (it.currency as? com.tangem.tap.features.wallet.redux.Currency.Token)?.token }.distinct()
|
||||
}
|
||||
|
||||
fun List<WalletData>.toTokensWithBlockchains(derivationStyle: DerivationStyle?): List<TokenWithBlockchain> {
|
||||
return mapNotNull {
|
||||
if (it.currency !is com.tangem.tap.features.wallet.redux.Currency.Token) return@mapNotNull null
|
||||
if (it.currency.isCustomCurrency(derivationStyle)) return@mapNotNull null
|
||||
TokenWithBlockchain(it.currency.token, it.currency.blockchain)
|
||||
}.distinct()
|
||||
}
|
||||
|
||||
fun List<WalletData>.toBlockchains(derivationStyle: DerivationStyle?): List<Blockchain> {
|
||||
return mapNotNull {
|
||||
if (it.currency.isCustomCurrency(derivationStyle)) {
|
||||
null
|
||||
} else {
|
||||
(it.currency as? com.tangem.tap.features.wallet.redux.Currency.Blockchain)?.blockchain
|
||||
}
|
||||
}.distinct()
|
||||
}
|
||||
|
||||
data class TokenWithBlockchain(
|
||||
val token: Token,
|
||||
val blockchain: Blockchain
|
||||
)
|
||||
|
||||
fun List<Currency>.filter(supportedBlockchains: Set<Blockchain>?): List<Currency> {
|
||||
if (supportedBlockchains == null) return this
|
||||
return map {
|
||||
it.copy(contracts =
|
||||
it.contracts?.filter {
|
||||
supportedBlockchains.contains(it.blockchain) && it.blockchain.canHandleTokens()
|
||||
}
|
||||
)
|
||||
}.filterNot {
|
||||
it.contracts.isNullOrEmpty() && !supportedBlockchains.contains(Blockchain.fromNetworkId(it.id))
|
||||
}
|
||||
}
|
||||
|
|
@ -8,15 +8,22 @@ import android.view.View
|
|||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionInflater
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.google.accompanist.appcompattheme.AppCompatTheme
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.extensions.copyToClipboard
|
||||
import com.tangem.tap.common.extensions.dispatchNotification
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.tokens.redux.ContractAddress
|
||||
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
|
||||
import com.tangem.tap.features.tokens.redux.TokensAction
|
||||
import com.tangem.tap.features.tokens.redux.TokensState
|
||||
import com.tangem.tap.features.tokens.ui.adapters.CurrenciesAdapter
|
||||
import com.tangem.tap.features.tokens.ui.compose.CurrenciesScreen
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -27,10 +34,11 @@ import org.rekotlin.StoreSubscriber
|
|||
|
||||
|
||||
class AddTokensFragment : Fragment(R.layout.fragment_add_tokens),
|
||||
StoreSubscriber<TokensState> {
|
||||
StoreSubscriber<TokensState> {
|
||||
|
||||
private lateinit var viewAdapter: CurrenciesAdapter
|
||||
private val binding: FragmentAddTokensBinding by viewBinding(FragmentAddTokensBinding::bind)
|
||||
private var tokensState: MutableState<TokensState> = mutableStateOf(store.state.tokensState)
|
||||
private var searchInput = mutableStateOf("")
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -64,27 +72,32 @@ class AddTokensFragment : Fragment(R.layout.fragment_add_tokens),
|
|||
|
||||
(activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
|
||||
toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
|
||||
setupPopularTokensRecyclerView()
|
||||
btnTokensSaveChanges.setOnClickListener {
|
||||
store.dispatch(TokensAction.SaveChanges(viewAdapter.getAddedItems()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupPopularTokensRecyclerView() = with(binding) {
|
||||
viewAdapter = CurrenciesAdapter()
|
||||
viewAdapter.setOnItemAddListener {
|
||||
btnTokensSaveChanges.isEnabled = viewAdapter.getAddedItems().isNotEmpty()
|
||||
val onSaveChanges = { tokens: List<TokenWithBlockchain>, blockchains: List<Blockchain> ->
|
||||
store.dispatch(TokensAction.SaveChanges(tokens, blockchains))
|
||||
}
|
||||
val onNetworkItemClicked = { contractAddress: ContractAddress ->
|
||||
context?.copyToClipboard(contractAddress)
|
||||
store.dispatchNotification(R.string.contract_address_copied_message)
|
||||
}
|
||||
|
||||
rvPopularTokens.layoutManager = LinearLayoutManager(context)
|
||||
rvPopularTokens.adapter = viewAdapter
|
||||
cvCurrencies.setContent {
|
||||
AppCompatTheme {
|
||||
CurrenciesScreen(
|
||||
tokensState = tokensState,
|
||||
searchInput = searchInput,
|
||||
onSaveChanges = onSaveChanges,
|
||||
onNetworkItemClicked = onNetworkItemClicked
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun newState(state: TokensState) {
|
||||
if (activity == null || view == null) return
|
||||
|
||||
viewAdapter.addedCurrencies = state.addedCurrencies
|
||||
viewAdapter.submitUnfilteredList(state.shownCurrencies)
|
||||
val toolbarTitle =
|
||||
if (state.allowToAdd) R.string.add_tokens_title else R.string.search_tokens_title
|
||||
binding.toolbar.title = getString(toolbarTitle)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
|
@ -102,13 +115,13 @@ class AddTokensFragment : Fragment(R.layout.fragment_add_tokens),
|
|||
searchView.queryHint = searchView.getString(R.string.add_token_search_hint)
|
||||
searchView.maxWidth = android.R.attr.width
|
||||
searchView.inputtedTextAsFlow()
|
||||
.debounce(400)
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
viewAdapter.filter(searchView.query)
|
||||
}
|
||||
.launchIn(mainScope)
|
||||
return super.onCreateOptionsMenu(menu, inflater);
|
||||
.debounce(400)
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
searchInput.value = it.lowercase()
|
||||
}
|
||||
.launchIn(mainScope)
|
||||
return super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
|
|||
|
|
@ -1,254 +0,0 @@
|
|||
package com.tangem.tap.features.tokens.ui.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.loadCurrenciesIcon
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.domain.tokens.CardCurrencies
|
||||
import com.tangem.tap.domain.tokens.getTokensName
|
||||
import com.tangem.tap.features.tokens.redux.CurrencyListItem
|
||||
import com.tangem.tap.features.tokens.redux.TokensAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.ItemCurrencySubtitleBinding
|
||||
import com.tangem.wallet.databinding.ItemPopularTokenBinding
|
||||
import java.util.*
|
||||
|
||||
class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>(DiffUtilCallback) {
|
||||
|
||||
var addedCurrencies: CardCurrencies? = null
|
||||
|
||||
private var unfilteredList = listOf<CurrencyListItem>()
|
||||
|
||||
private val currentlyAddedItems = mutableListOf<CurrencyListItem>()
|
||||
|
||||
private var vhOnItemAddListener: ((CurrencyListItem) -> Unit) = {
|
||||
currentlyAddedItems.add(it)
|
||||
onItemAddListener?.invoke()
|
||||
}
|
||||
private var onItemAddListener: VoidCallback? = null
|
||||
|
||||
fun submitUnfilteredList(list: List<CurrencyListItem>) {
|
||||
unfilteredList = list
|
||||
submitList(list)
|
||||
}
|
||||
|
||||
fun setOnItemAddListener(itemAddListener: VoidCallback) {
|
||||
onItemAddListener = itemAddListener
|
||||
if (currentlyAddedItems.isNotEmpty()) itemAddListener()
|
||||
}
|
||||
|
||||
fun getAddedItems(): List<CurrencyListItem> {
|
||||
return currentlyAddedItems.toList()
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (currentList[position]) {
|
||||
is CurrencyListItem.TitleListItem -> 0
|
||||
is CurrencyListItem.BlockchainListItem, is CurrencyListItem.TokenListItem -> 1
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
0 -> TitleViewHolder(
|
||||
binding = ItemCurrencySubtitleBinding
|
||||
.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
1 -> CurrenciesViewHolder(
|
||||
binding = ItemPopularTokenBinding
|
||||
.inflate(LayoutInflater.from(parent.context), parent, false),
|
||||
onItemAddListener = vhOnItemAddListener
|
||||
)
|
||||
else -> CurrenciesViewHolder(
|
||||
binding = ItemPopularTokenBinding
|
||||
.inflate(LayoutInflater.from(parent.context), parent, false),
|
||||
onItemAddListener = vhOnItemAddListener
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val listItem = currentList[position]
|
||||
if (holder is TitleViewHolder && listItem is CurrencyListItem.TitleListItem) {
|
||||
holder.bind(listItem)
|
||||
} else if (holder is CurrenciesViewHolder) {
|
||||
holder.bind(listItem, addedCurrencies, currentlyAddedItems)
|
||||
}
|
||||
}
|
||||
|
||||
object DiffUtilCallback : DiffUtil.ItemCallback<CurrencyListItem>() {
|
||||
override fun areContentsTheSame(
|
||||
oldItem: CurrencyListItem, newItem: CurrencyListItem,
|
||||
) = oldItem == newItem
|
||||
|
||||
override fun areItemsTheSame(
|
||||
oldItem: CurrencyListItem, newItem: CurrencyListItem,
|
||||
) = oldItem == newItem
|
||||
}
|
||||
|
||||
fun filter(query: CharSequence?) {
|
||||
val list = mutableListOf<CurrencyListItem>()
|
||||
|
||||
if (!query.isNullOrEmpty()) {
|
||||
store.dispatch(TokensAction.OpenAllTokens)
|
||||
val queryNormalized = query.toString().toLowerCase(Locale.US)
|
||||
list.addAll(
|
||||
unfilteredList.filter { element ->
|
||||
when (element) {
|
||||
is CurrencyListItem.BlockchainListItem -> {
|
||||
element.blockchain.currency.toLowerCase(Locale.US)
|
||||
.contains(queryNormalized) ||
|
||||
element.blockchain.fullName.toLowerCase(Locale.US)
|
||||
.contains(queryNormalized)
|
||||
|
||||
}
|
||||
is CurrencyListItem.TokenListItem -> {
|
||||
element.token.name.toLowerCase(Locale.US)
|
||||
.contains(queryNormalized) ||
|
||||
element.token.symbol.toLowerCase(Locale.US)
|
||||
.contains(queryNormalized)
|
||||
}
|
||||
is CurrencyListItem.TitleListItem -> true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
list.addAll(unfilteredList)
|
||||
}
|
||||
submitList(list)
|
||||
}
|
||||
|
||||
class CurrenciesViewHolder(
|
||||
private val binding: ItemPopularTokenBinding,
|
||||
private val onItemAddListener: ((CurrencyListItem) -> Unit),
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(
|
||||
currency: CurrencyListItem,
|
||||
addedCurrencies: CardCurrencies?,
|
||||
currentlyAddedItems: MutableList<CurrencyListItem>
|
||||
) {
|
||||
when (currency) {
|
||||
is CurrencyListItem.BlockchainListItem -> {
|
||||
val addedBlockchains = addedCurrencies?.blockchains ?: listOf()
|
||||
val currentlyAdded =
|
||||
currentlyAddedItems.filterIsInstance<CurrencyListItem.BlockchainListItem>()
|
||||
bindBlockchain(currency, addedBlockchains, currentlyAdded)
|
||||
}
|
||||
is CurrencyListItem.TokenListItem -> {
|
||||
val addedTokens = addedCurrencies?.tokens ?: listOf()
|
||||
val currentlyAdded =
|
||||
currentlyAddedItems.filterIsInstance<CurrencyListItem.TokenListItem>()
|
||||
bindToken(currency, addedTokens, currentlyAdded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindBlockchain(
|
||||
currency: CurrencyListItem.BlockchainListItem,
|
||||
addedBlockchains: List<Blockchain>,
|
||||
currentlyAdded: List<CurrencyListItem.BlockchainListItem>
|
||||
) = with(binding) {
|
||||
val blockchain = currency.blockchain
|
||||
Picasso.get().loadCurrenciesIcon(ivCurrency, tvTokenLetter, null, blockchain)
|
||||
|
||||
tvCurrencyName.text = blockchain.fullName
|
||||
tvCurrencySymbol.text = blockchain.currency
|
||||
btnAddToken.setOnClickListener {
|
||||
onItemAddListener.invoke(currency)
|
||||
currency.isAdded = true
|
||||
modifyAddTokenButton(currency)
|
||||
}
|
||||
|
||||
val isAddedBefore = addedBlockchains.contains(blockchain)
|
||||
val isCurrentlyAdded = currentlyAdded.any { it.blockchain == blockchain }
|
||||
currency.isAdded = isAddedBefore || isCurrentlyAdded
|
||||
modifyAddTokenButton(currency)
|
||||
}
|
||||
|
||||
private fun bindToken(
|
||||
currency: CurrencyListItem.TokenListItem,
|
||||
addedTokens: List<Token>,
|
||||
currentlyAdded: List<CurrencyListItem.TokenListItem>
|
||||
) = with(binding) {
|
||||
val token = currency.token
|
||||
Picasso.get()
|
||||
.loadCurrenciesIcon(ivCurrency, tvTokenLetter, token, token.blockchain)
|
||||
|
||||
tvCurrencyName.text = token.name
|
||||
tvCurrencySymbol.text = tvCurrencySymbol.getString(
|
||||
R.string.token_symbol_address_format,
|
||||
token.symbol, token.contractAddress
|
||||
)
|
||||
btnAddToken.setOnClickListener {
|
||||
onItemAddListener.invoke(currency)
|
||||
currency.isAdded = true
|
||||
modifyAddTokenButton(currency)
|
||||
}
|
||||
|
||||
val isAddedBefore = addedTokens.any { it == token }
|
||||
val isCurrentlyAdded = currentlyAdded.any { it.token == token }
|
||||
currency.isAdded = isAddedBefore || isCurrentlyAdded
|
||||
|
||||
modifyAddTokenButton(currency)
|
||||
}
|
||||
|
||||
private fun modifyAddTokenButton(currency: CurrencyListItem) = with(binding) {
|
||||
if (currency.isLocked) {
|
||||
btnAddToken.setText(R.string.common_add)
|
||||
btnAddToken.isEnabled = false
|
||||
} else {
|
||||
val text = if (currency.isAdded) R.string.add_token_added else R.string.common_add
|
||||
btnAddToken.setText(text)
|
||||
btnAddToken.isEnabled = !currency.isAdded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TitleViewHolder(val binding: ItemCurrencySubtitleBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(title: CurrencyListItem.TitleListItem) = with(binding) {
|
||||
if (title.blockchain != null) {
|
||||
val subtitle = root.getString(title.titleResId, title.blockchain.getTokensName())
|
||||
tvSubtitle.text = subtitle.uppercase()
|
||||
clSubtitleContainer.setOnClickListener {
|
||||
val rotation = if (title.isContentShown) -90f else 90f
|
||||
store.dispatch(
|
||||
TokensAction.ToggleShowTokensForBlockchain(
|
||||
title.isContentShown, title.blockchain
|
||||
)
|
||||
)
|
||||
ivToggleSublistVisibility.animate().rotation(rotation).withEndAction {
|
||||
showArrow(title.isContentShown)
|
||||
}
|
||||
}
|
||||
showArrow(title.isContentShown)
|
||||
} else {
|
||||
tvSubtitle.text = root.getString(title.titleResId).uppercase()
|
||||
ivToggleSublistVisibility.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showArrow(isContentShown: Boolean) = with(binding) {
|
||||
ivToggleSublistVisibility.show()
|
||||
val drawable = if (isContentShown) {
|
||||
R.drawable.ic_arrow_angle_down
|
||||
} else {
|
||||
R.drawable.ic_arrow_angle_right
|
||||
}
|
||||
ivToggleSublistVisibility.setImageResource(drawable)
|
||||
ivToggleSublistVisibility.rotation = 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package com.tangem.tap.features.tokens.ui.compose
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.extensions.getGreyedOutIconRes
|
||||
import com.tangem.tap.common.extensions.getRoundIconRes
|
||||
import com.tangem.tap.domain.tokens.Currency
|
||||
import com.tangem.tap.domain.tokens.fromNetworkId
|
||||
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
fun CollapsedCurrencyItem(
|
||||
currency: Currency,
|
||||
addedTokens: List<TokenWithBlockchain>,
|
||||
addedBlockchains: List<Blockchain>,
|
||||
onCurrencyClick: (String) -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { onCurrencyClick(currency.id) })
|
||||
) {
|
||||
val blockchain = Blockchain.fromNetworkId(currency.id)
|
||||
val iconRes = currency.iconUrl
|
||||
|
||||
SubcomposeAsyncImage(
|
||||
model = iconRes,
|
||||
contentDescription = currency.id,
|
||||
loading = { CurrencyPlaceholderIcon(currency.id) },
|
||||
error = { CurrencyPlaceholderIcon(currency.id) },
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.padding(16.dp)
|
||||
.size(46.dp),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f)
|
||||
.align(Alignment.CenterVertically)
|
||||
) {
|
||||
Text(
|
||||
text = currency.name,
|
||||
fontSize = 17.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF1C1C1E),
|
||||
)
|
||||
Spacer(modifier = Modifier.size(6.dp))
|
||||
Row {
|
||||
if (!currency.contracts.isNullOrEmpty()) {
|
||||
currency.contracts.map { contract ->
|
||||
if (contract.address == currency.symbol) {
|
||||
BlockchainNetworkItem(
|
||||
blockchain = Blockchain.fromNetworkId(contract.networkId),
|
||||
addedBlockchains = addedBlockchains
|
||||
)
|
||||
} else {
|
||||
val added =
|
||||
addedTokens.map { it.token.contractAddress }
|
||||
.contains(contract.address)
|
||||
val icon = if (added) {
|
||||
contract.blockchain.getRoundIconRes()
|
||||
} else {
|
||||
contract.blockchain.getGreyedOutIconRes()
|
||||
}
|
||||
Image(
|
||||
painter = painterResource(id = icon),
|
||||
contentDescription = null,
|
||||
Modifier
|
||||
.size(20.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.size(5.dp))
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
BlockchainNetworkItem(
|
||||
blockchain = blockchain,
|
||||
addedBlockchains = addedBlockchains
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_arrow_collapsed),
|
||||
contentDescription = null,
|
||||
Modifier
|
||||
.padding(20.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BlockchainNetworkItem(
|
||||
blockchain: Blockchain?,
|
||||
addedBlockchains: List<Blockchain>
|
||||
) {
|
||||
val added = addedBlockchains.contains(blockchain)
|
||||
val icon =
|
||||
if (added) blockchain?.getRoundIconRes() else blockchain?.getGreyedOutIconRes()
|
||||
if (icon != null) {
|
||||
Image(
|
||||
painter = painterResource(id = icon),
|
||||
contentDescription = null,
|
||||
Modifier
|
||||
.size(20.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.size(5.dp))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package com.tangem.tap.features.tokens.ui.compose
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.ExtendedFloatingActionButton
|
||||
import androidx.compose.material.FabPosition
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.compose.Keyboard
|
||||
import com.tangem.tap.common.compose.keyboardAsState
|
||||
import com.tangem.tap.common.extensions.pixelsToDp
|
||||
import com.tangem.tap.domain.tokens.Currency
|
||||
import com.tangem.tap.domain.tokens.fromNetworkId
|
||||
import com.tangem.tap.features.tokens.redux.ContractAddress
|
||||
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
|
||||
import com.tangem.tap.features.tokens.redux.TokensState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
fun CurrenciesScreen(
|
||||
tokensState: MutableState<TokensState> = mutableStateOf(store.state.tokensState),
|
||||
searchInput: MutableState<String>,
|
||||
onSaveChanges: (List<TokenWithBlockchain>, List<Blockchain>) -> Unit,
|
||||
onNetworkItemClicked: (ContractAddress) -> Unit
|
||||
) {
|
||||
|
||||
val addedTokensState = remember { mutableStateOf(tokensState.value.addedTokens) }
|
||||
val addedBlockchainsState = remember { mutableStateOf(tokensState.value.addedBlockchains) }
|
||||
|
||||
val isKeyboardOpen by keyboardAsState()
|
||||
|
||||
val onAddCurrencyToggleClick = { currency: Currency, token: TokenWithBlockchain? ->
|
||||
if (token != null) {
|
||||
val mutableList = addedTokensState.value.toMutableList()
|
||||
if (mutableList.contains(token)) {
|
||||
mutableList.remove(token)
|
||||
} else {
|
||||
mutableList.add(token)
|
||||
}
|
||||
addedTokensState.value = mutableList
|
||||
} else {
|
||||
val blockchain = Blockchain.fromNetworkId(currency.id)
|
||||
val mutableList = addedBlockchainsState.value.toMutableList()
|
||||
if (mutableList.contains(blockchain)) {
|
||||
mutableList.remove(blockchain)
|
||||
} else {
|
||||
blockchain?.let { mutableList.add(blockchain) }
|
||||
}
|
||||
addedBlockchainsState.value = mutableList
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
// topBar =,
|
||||
floatingActionButton = {
|
||||
if (tokensState.value.allowToAdd) SaveChangesButton(isKeyboardOpen) {
|
||||
onSaveChanges(addedTokensState.value, addedBlockchainsState.value)
|
||||
}
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.Center,
|
||||
) {
|
||||
|
||||
Column {
|
||||
if (tokensState.value.allowToAdd) CurrenciesWarning()
|
||||
ListOfCurrencies(
|
||||
currencies = tokensState.value.currencies,
|
||||
nonRemovableTokens = tokensState.value.nonRemovableTokens,
|
||||
nonRemovableBlockchains = tokensState.value.nonRemovableBlockchains,
|
||||
addedTokens = addedTokensState.value,
|
||||
addedBlockchains = addedBlockchainsState.value,
|
||||
searchInput = searchInput.value,
|
||||
allowToAdd = tokensState.value.allowToAdd,
|
||||
onAddCurrencyToggled = onAddCurrencyToggleClick,
|
||||
onNetworkItemClicked = onNetworkItemClicked
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SaveChangesButton(keyboardState: Keyboard, onSaveChanges: () -> Unit) {
|
||||
|
||||
val padding = if (keyboardState is Keyboard.Opened) {
|
||||
LocalContext.current.pixelsToDp(keyboardState.height)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
ExtendedFloatingActionButton(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.common_save_changes),
|
||||
)
|
||||
},
|
||||
onClick = onSaveChanges,
|
||||
backgroundColor = Color(0xFF1ACE80),
|
||||
contentColor = Color.White,
|
||||
modifier = Modifier.padding(bottom = padding.dp)
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tangem.tap.features.tokens.ui.compose
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
fun CurrenciesWarning() {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.background(Color(0xFFF2F2F2), shape = RoundedCornerShape(10.dp))
|
||||
)
|
||||
{
|
||||
val warning = stringResource(id = R.string.alert_manage_tokens_addresses_message)
|
||||
val end = warning.indexOf(" ")
|
||||
val spanStyles = listOf(
|
||||
AnnotatedString.Range(
|
||||
SpanStyle(fontWeight = FontWeight.Bold),
|
||||
start = 0,
|
||||
end = end
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = AnnotatedString(text = warning, spanStyles = spanStyles),
|
||||
textAlign = TextAlign.Center,
|
||||
color = Color(0xFF848488),
|
||||
modifier = Modifier.padding(
|
||||
top = 8.dp,
|
||||
bottom = 8.dp,
|
||||
start = 16.dp,
|
||||
end = 16.dp
|
||||
),
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.tap.features.tokens.ui.compose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.domain.tokens.Currency
|
||||
import com.tangem.tap.features.tokens.redux.ContractAddress
|
||||
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
|
||||
|
||||
@Composable
|
||||
fun CurrencyItem(
|
||||
currency: Currency,
|
||||
nonRemovableTokens: List<ContractAddress>,
|
||||
nonRemovableBlockchains: List<Blockchain>,
|
||||
addedTokens: List<TokenWithBlockchain>,
|
||||
addedBlockchains: List<Blockchain>,
|
||||
allowToAdd: Boolean,
|
||||
expanded: Boolean,
|
||||
onCurrencyClick: (String) -> Unit,
|
||||
onAddCurrencyToggled: (Currency, TokenWithBlockchain?) -> Unit,
|
||||
onNetworkItemClicked: (ContractAddress) -> Unit
|
||||
) {
|
||||
if (expanded) {
|
||||
ExpandedCurrencyItem(
|
||||
currency = currency,
|
||||
nonRemovableTokens = nonRemovableTokens,
|
||||
nonRemovableBlockchains = nonRemovableBlockchains,
|
||||
addedTokens = addedTokens,
|
||||
addedBlockchains = addedBlockchains,
|
||||
allowToAdd = allowToAdd,
|
||||
onCurrencyClick = onCurrencyClick, onAddCurrencyToggled = onAddCurrencyToggled,
|
||||
onNetworkItemClicked = onNetworkItemClicked
|
||||
)
|
||||
} else {
|
||||
CollapsedCurrencyItem(
|
||||
currency = currency, addedTokens = addedTokens,
|
||||
addedBlockchains = addedBlockchains, onCurrencyClick
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.tap.features.tokens.ui.compose
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@Composable
|
||||
fun CurrencyPlaceholderIcon(id: String) {
|
||||
|
||||
val letterColor: Color = Color.White
|
||||
val circleColor: Color = Color.Black
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.background(circleColor, shape = CircleShape)
|
||||
)
|
||||
{
|
||||
Text(
|
||||
text = id.firstOrNull()?.titlecase() ?: "",
|
||||
textAlign = TextAlign.Center,
|
||||
color = letterColor,
|
||||
modifier = Modifier.padding(4.dp),
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
package com.tangem.tap.features.tokens.ui.compose
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.domain.tokens.Currency
|
||||
import com.tangem.tap.domain.tokens.fromNetworkId
|
||||
import com.tangem.tap.features.tokens.redux.ContractAddress
|
||||
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
fun ExpandedCurrencyItem(
|
||||
currency: Currency,
|
||||
nonRemovableTokens: List<ContractAddress>,
|
||||
nonRemovableBlockchains: List<Blockchain>,
|
||||
addedTokens: List<TokenWithBlockchain>,
|
||||
addedBlockchains: List<Blockchain>,
|
||||
allowToAdd: Boolean,
|
||||
onCurrencyClick: (String) -> Unit,
|
||||
onAddCurrencyToggled: (Currency, TokenWithBlockchain?) -> Unit,
|
||||
onNetworkItemClicked: (ContractAddress) -> Unit
|
||||
) {
|
||||
val blockchain = Blockchain.fromNetworkId(currency.id)
|
||||
val iconRes = currency.iconUrl
|
||||
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { onCurrencyClick(currency.id) })
|
||||
) {
|
||||
SubcomposeAsyncImage(
|
||||
model = iconRes,
|
||||
contentDescription = currency.id,
|
||||
loading = { CurrencyPlaceholderIcon(currency.id) },
|
||||
error = { CurrencyPlaceholderIcon(currency.id) },
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.padding(start = 16.dp, top = 16.dp, end = 16.dp)
|
||||
.size(46.dp),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f)
|
||||
.padding(top = 14.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
) {
|
||||
Text(
|
||||
text = currency.name,
|
||||
fontSize = 17.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF1C1C1E),
|
||||
)
|
||||
Spacer(modifier = Modifier.size(6.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.currency_subtitle_expanded),
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF8E8E93),
|
||||
)
|
||||
}
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_arrow_extended),
|
||||
contentDescription = null,
|
||||
Modifier
|
||||
.padding(20.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
|
||||
val blockchains = currency.contracts?.map { it.blockchain } ?: listOfNotNull(
|
||||
Blockchain.fromNetworkId(currency.id)
|
||||
)
|
||||
|
||||
Row {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
if (blockchains.size > 1) {
|
||||
val dividerHeight = if (blockchains.size == 2) {
|
||||
60
|
||||
} else if (blockchains.size == 3) {
|
||||
110
|
||||
} else {
|
||||
43 * blockchains.size
|
||||
}
|
||||
Divider(
|
||||
color = Color(0xFFDEDEDE),
|
||||
modifier = Modifier
|
||||
.height(dividerHeight.dp)
|
||||
.padding(start = 37.5.dp)
|
||||
.width(1.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Column {
|
||||
blockchains.map {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_link),
|
||||
contentDescription = null,
|
||||
Modifier
|
||||
.padding(
|
||||
start = 37.dp,
|
||||
)
|
||||
)
|
||||
Spacer(modifier = Modifier.size(13.5.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 6.dp),
|
||||
) {
|
||||
blockchains.map { blockchain ->
|
||||
val contract = currency.contracts?.firstOrNull { it.blockchain == blockchain }
|
||||
val added = if (contract != null && contract.address != currency.symbol) {
|
||||
addedTokens.map { it.token.contractAddress }.contains(contract.address)
|
||||
} else {
|
||||
addedBlockchains.contains(blockchain)
|
||||
}
|
||||
val canBeRemoved = if (contract != null && contract.address != currency.symbol) {
|
||||
!nonRemovableTokens.contains(contract.address)
|
||||
} else {
|
||||
!nonRemovableBlockchains.contains(blockchain)
|
||||
}
|
||||
NetworkItem(
|
||||
currency = currency,
|
||||
contract = contract,
|
||||
blockchain = blockchain, allowToAdd = allowToAdd,
|
||||
added = added,
|
||||
canBeRemoved = canBeRemoved,
|
||||
onAddCurrencyToggled = onAddCurrencyToggled,
|
||||
onNetworkItemClicked = onNetworkItemClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.tangem.tap.features.tokens.ui.compose
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.domain.tokens.Currency
|
||||
import com.tangem.tap.features.tokens.redux.ContractAddress
|
||||
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
|
||||
|
||||
|
||||
@Composable
|
||||
fun ListOfCurrencies(
|
||||
currencies: List<Currency>,
|
||||
nonRemovableTokens: List<ContractAddress>,
|
||||
nonRemovableBlockchains: List<Blockchain>,
|
||||
addedTokens: List<TokenWithBlockchain>,
|
||||
addedBlockchains: List<Blockchain>,
|
||||
searchInput: String,
|
||||
allowToAdd: Boolean,
|
||||
onAddCurrencyToggled: (Currency, TokenWithBlockchain?) -> Unit,
|
||||
onNetworkItemClicked: (ContractAddress) -> Unit
|
||||
) {
|
||||
|
||||
val expandedCurrencies = remember { mutableStateOf(listOf("")) }
|
||||
|
||||
val onCurrencyClick = { currencyId: String ->
|
||||
val mutableList = expandedCurrencies.value.toMutableList()
|
||||
if (mutableList.contains(currencyId)) {
|
||||
mutableList.remove(currencyId)
|
||||
} else {
|
||||
mutableList.add(currencyId)
|
||||
}
|
||||
expandedCurrencies.value = mutableList
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
|
||||
val filteredCurrencies = if (searchInput.isBlank()) {
|
||||
currencies
|
||||
} else {
|
||||
currencies.asSequence().filter {
|
||||
it.name.lowercase().contains(searchInput) || it.symbol.lowercase()
|
||||
.contains(searchInput)
|
||||
}.toList()
|
||||
}
|
||||
items(filteredCurrencies) { currency ->
|
||||
CurrencyItem(
|
||||
currency = currency,
|
||||
nonRemovableTokens = nonRemovableTokens,
|
||||
nonRemovableBlockchains = nonRemovableBlockchains,
|
||||
addedTokens = addedTokens,
|
||||
addedBlockchains = addedBlockchains,
|
||||
allowToAdd = allowToAdd,
|
||||
expanded = expandedCurrencies.value.contains(currency.id),
|
||||
onCurrencyClick = onCurrencyClick,
|
||||
onAddCurrencyToggled = onAddCurrencyToggled,
|
||||
onNetworkItemClicked = onNetworkItemClicked
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package com.tangem.tap.features.tokens.ui.compose
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Switch
|
||||
import androidx.compose.material.SwitchDefaults
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.tap.common.extensions.getGreyedOutIconRes
|
||||
import com.tangem.tap.common.extensions.getNetworkName
|
||||
import com.tangem.tap.common.extensions.getRoundIconRes
|
||||
import com.tangem.tap.domain.tokens.Contract
|
||||
import com.tangem.tap.domain.tokens.Currency
|
||||
import com.tangem.tap.features.tokens.redux.ContractAddress
|
||||
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun NetworkItem(
|
||||
currency: Currency, contract: Contract?,
|
||||
blockchain: Blockchain, allowToAdd: Boolean,
|
||||
added: Boolean, canBeRemoved: Boolean,
|
||||
onAddCurrencyToggled: (Currency, TokenWithBlockchain?) -> Unit,
|
||||
onNetworkItemClicked: (ContractAddress) -> Unit
|
||||
) {
|
||||
|
||||
val isBlockchain = contract == null || contract.address == currency.symbol
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.combinedClickable {
|
||||
if (contract != null) onNetworkItemClicked(contract.address)
|
||||
}
|
||||
) {
|
||||
SubcomposeAsyncImage(
|
||||
model = if (added) blockchain.getRoundIconRes() else blockchain.getGreyedOutIconRes(),
|
||||
contentDescription = blockchain.fullName,
|
||||
loading = { CurrencyPlaceholderIcon(blockchain.id) },
|
||||
error = { CurrencyPlaceholderIcon(blockchain.id) },
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.padding(start = 8.dp, top = 16.dp, bottom = 16.dp, end = 6.dp)
|
||||
.size(20.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f)
|
||||
.align(Alignment.CenterVertically)
|
||||
) {
|
||||
Text(
|
||||
text = blockchain.name.uppercase(),
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = if (added) Color.Black else Color(0xFF848488),
|
||||
)
|
||||
Spacer(modifier = Modifier.size(3.dp))
|
||||
Text(
|
||||
text = if (isBlockchain) "MAIN" else blockchain.getNetworkName().uppercase(),
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = if (!isBlockchain) Color(0xFF8E8E93) else Color(0xFF1ACE80),
|
||||
)
|
||||
}
|
||||
|
||||
if (allowToAdd) {
|
||||
val token = if (!isBlockchain) {
|
||||
Token(
|
||||
id = currency.id,
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
contractAddress = contract!!.address,
|
||||
decimals = contract.decimalCount,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val tokenWithBlockchain =
|
||||
token?.let { TokenWithBlockchain(token, contract!!.blockchain) }
|
||||
|
||||
val currencyToSave = if (isBlockchain && contract != null) {
|
||||
currency.copy(id = contract.networkId)
|
||||
} else {
|
||||
currency
|
||||
}
|
||||
|
||||
Switch(
|
||||
checked = added,
|
||||
enabled = canBeRemoved,
|
||||
onCheckedChange = { onAddCurrencyToggled(currencyToSave, tokenWithBlockchain) },
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp),
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = Color(0xFF1ACE80)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import com.tangem.tap.common.redux.ErrorAction
|
|||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.tokens.CardCurrencies
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -24,9 +24,14 @@ sealed class WalletAction : Action {
|
|||
}
|
||||
|
||||
|
||||
data class LoadWallet(val blockchain: Blockchain? = null) : WalletAction() {
|
||||
data class Success(val wallet: Wallet) : WalletAction()
|
||||
data class NoAccount(val wallet: Wallet, val amountToCreateAccount: String) : WalletAction()
|
||||
data class LoadWallet(val blockchain: BlockchainNetwork? = null) : WalletAction() {
|
||||
data class Success(val wallet: Wallet, val blockchain: BlockchainNetwork) : WalletAction()
|
||||
data class NoAccount(
|
||||
val wallet: Wallet,
|
||||
val blockchain: BlockchainNetwork,
|
||||
val amountToCreateAccount: String
|
||||
) : WalletAction()
|
||||
|
||||
data class Failure(val wallet: Wallet, val errorMessage: String? = null) : WalletAction()
|
||||
}
|
||||
|
||||
|
|
@ -35,19 +40,30 @@ sealed class WalletAction : Action {
|
|||
|
||||
sealed class MultiWallet : WalletAction() {
|
||||
data class SetIsMultiwalletAllowed(val isMultiwalletAllowed: Boolean) : MultiWallet()
|
||||
data class AddWalletManagers(val walletManagers: List<WalletManager>) : MultiWallet() {
|
||||
constructor(walletManager: WalletManager) : this(listOf(walletManager))
|
||||
}
|
||||
|
||||
data class AddBlockchain(val blockchain: Blockchain) : MultiWallet()
|
||||
data class AddBlockchains(val blockchains: List<Blockchain>) : MultiWallet()
|
||||
data class AddTokens(val tokens: List<Token>) : MultiWallet()
|
||||
data class AddToken(val token: Token) : MultiWallet()
|
||||
data class SaveCurrencies(val cardCurrencies: CardCurrencies) : MultiWallet()
|
||||
data class AddBlockchain(
|
||||
val blockchain: BlockchainNetwork,
|
||||
val walletManager: WalletManager?
|
||||
) : MultiWallet()
|
||||
|
||||
data class AddBlockchains(
|
||||
val blockchains: List<BlockchainNetwork>, val walletManagers: List<WalletManager>
|
||||
) : MultiWallet()
|
||||
|
||||
data class AddTokens(val tokens: List<Token>, val blockchain: BlockchainNetwork) :
|
||||
MultiWallet()
|
||||
|
||||
data class AddToken(val token: Token, val blockchain: BlockchainNetwork) : MultiWallet()
|
||||
data class SaveCurrencies(val blockchainNetworks: List<BlockchainNetwork>) : MultiWallet()
|
||||
object FindTokensInUse : MultiWallet()
|
||||
object FindBlockchainsInUse : MultiWallet()
|
||||
|
||||
data class TokenLoaded(val amount: Amount, val token: Token) : MultiWallet()
|
||||
data class TokenLoaded(
|
||||
val amount: Amount,
|
||||
val token: Token,
|
||||
val blockchain: BlockchainNetwork
|
||||
) : MultiWallet()
|
||||
|
||||
data class SelectWallet(val walletData: WalletData?) : MultiWallet()
|
||||
data class RemoveWallet(val walletData: WalletData) : MultiWallet()
|
||||
data class SetPrimaryBlockchain(val blockchain: Blockchain) : MultiWallet()
|
||||
|
|
@ -135,5 +151,9 @@ sealed class WalletAction : Action {
|
|||
|
||||
data class ChangeSelectedAddress(val type: AddressType) : WalletAction()
|
||||
|
||||
data class SetWalletRent(val blockchain: Blockchain, val minRent: String, val rentExempt: String) : WalletAction()
|
||||
data class SetWalletRent(
|
||||
val blockchain: BlockchainNetwork,
|
||||
val minRent: String,
|
||||
val rentExempt: String
|
||||
) : WalletAction()
|
||||
}
|
||||
|
|
@ -10,10 +10,12 @@ import com.tangem.tap.common.extensions.toQrCode
|
|||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.common.redux.global.CryptoCurrencyName
|
||||
import com.tangem.tap.common.toggleWidget.WidgetState
|
||||
import com.tangem.tap.domain.TapWorkarounds.derivationStyle
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.extensions.buyIsAllowed
|
||||
import com.tangem.tap.domain.extensions.sellIsAllowed
|
||||
import com.tangem.tap.domain.extensions.toSendableAmounts
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.models.toPendingTransactions
|
||||
|
|
@ -33,8 +35,7 @@ data class WalletState(
|
|||
val hashesCountVerified: Boolean? = null,
|
||||
val walletDialog: StateDialog? = null,
|
||||
val mainWarningsList: List<WarningMessage> = mutableListOf(),
|
||||
val walletsData: List<WalletData> = emptyList(),
|
||||
val walletManagers: List<WalletManager> = emptyList(),
|
||||
val wallets: List<WalletStore> = listOf(),
|
||||
val isMultiwalletAllowed: Boolean = false,
|
||||
val cardCurrency: CryptoCurrencyName? = null,
|
||||
val selectedCurrency: Currency? = null,
|
||||
|
|
@ -52,45 +53,96 @@ data class WalletState(
|
|||
val isTangemTwins: Boolean
|
||||
get() = store.state.globalState.scanResponse?.isTangemTwins() == true
|
||||
|
||||
val primaryWallet = if (walletsData.isNotEmpty()) walletsData[0] else null
|
||||
val primaryWallet: WalletData? = if (wallets.isNotEmpty()) wallets[0].walletsData[0] else null
|
||||
val primaryWalletManager: WalletManager? =
|
||||
if (wallets.isNotEmpty()) wallets[0].walletManager else null
|
||||
|
||||
val shouldShowDetails: Boolean =
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain
|
||||
|
||||
val blockchains: List<Blockchain>
|
||||
get() = walletManagers.map { it.wallet.blockchain }
|
||||
get() = wallets.mapNotNull { it.walletManager?.wallet?.blockchain }
|
||||
|
||||
val currencies: List<Currency>
|
||||
get() = walletsData.mapNotNull { it.currency }
|
||||
get() = wallets.flatMap { it.walletsData }.map { it.currency }
|
||||
|
||||
val walletsData: List<WalletData>
|
||||
get() = wallets.flatMap { it.walletsData }
|
||||
|
||||
val walletManagers: List<WalletManager>
|
||||
get() = wallets.mapNotNull { it.walletManager }
|
||||
|
||||
fun getWalletManager(token: Token?): WalletManager? {
|
||||
if (token == null) return null
|
||||
return walletManagers.find { it.wallet.blockchain == token.blockchain }
|
||||
return wallets
|
||||
.mapNotNull { it.walletManager }
|
||||
.find { walletManager ->
|
||||
walletManager.cardTokens.any { it.contractAddress == token.contractAddress }
|
||||
}
|
||||
}
|
||||
|
||||
fun getWalletManager(currency: Currency?): WalletManager? {
|
||||
if (currency?.blockchain == null) return null
|
||||
return walletManagers.find { it.wallet.blockchain == currency.blockchain }
|
||||
return wallets.map { it.walletManager }
|
||||
.find { it?.wallet?.blockchain == currency.blockchain }
|
||||
}
|
||||
|
||||
fun getWalletManager(blockchain: Blockchain): WalletManager? {
|
||||
return walletManagers.find { it.wallet.blockchain == blockchain }
|
||||
fun getWalletManager(blockchain: BlockchainNetwork): WalletManager? {
|
||||
return wallets.find { it.blockchainNetwork == blockchain }?.walletManager
|
||||
}
|
||||
|
||||
fun getWalletData(blockchain: BlockchainNetwork?): WalletData? {
|
||||
if (blockchain == null) return null
|
||||
return walletsData.find {
|
||||
it.currency is Currency.Blockchain &&
|
||||
it.currency.blockchain == blockchain.blockchain &&
|
||||
it.currency.derivationPath == blockchain.derivationPath
|
||||
}
|
||||
}
|
||||
|
||||
fun getWalletStore(currency: Currency?): WalletStore? {
|
||||
if (currency == null) return null
|
||||
return wallets.firstOrNull {
|
||||
it.blockchainNetwork.derivationPath == currency.derivationPath &&
|
||||
(it.blockchainNetwork.blockchain == currency.blockchain)
|
||||
}
|
||||
}
|
||||
|
||||
fun getWalletStore(wallet: Wallet?): WalletStore? {
|
||||
if (wallet == null) return null
|
||||
val currency =
|
||||
Currency.Blockchain(wallet.blockchain, wallet.publicKey.derivationPath?.rawPath)
|
||||
return getWalletStore(currency)
|
||||
}
|
||||
|
||||
fun getWalletStore(blockchainNetwork: BlockchainNetwork?): WalletStore? {
|
||||
if (blockchainNetwork == null) return null
|
||||
return wallets.firstOrNull {
|
||||
it.blockchainNetwork.derivationPath == blockchainNetwork.derivationPath &&
|
||||
(it.blockchainNetwork.blockchain == blockchainNetwork.blockchain)
|
||||
}
|
||||
}
|
||||
|
||||
fun getWalletData(currency: Currency?): WalletData? {
|
||||
if (currency == null) return null
|
||||
return walletsData.find { it.currency == currency }
|
||||
}
|
||||
|
||||
fun getWalletData(blockchain: Blockchain?): WalletData? {
|
||||
if (blockchain == null) return null
|
||||
return walletsData.find { (it.currency as? Currency.Blockchain)?.blockchain == blockchain }
|
||||
return getWalletStore(currency)?.walletsData?.firstOrNull { it.currency == currency }
|
||||
}
|
||||
|
||||
fun getWalletData(token: Token?): WalletData? {
|
||||
if (token == null) return null
|
||||
return walletsData.find { (it.currency as? Currency.Token)?.token == token }
|
||||
return walletsData.find {
|
||||
(it.currency as? Currency.Token)?.token == token &&
|
||||
!it.currency.isCustomCurrency(store.state.globalState.scanResponse!!.card.derivationStyle)
|
||||
}
|
||||
}
|
||||
|
||||
fun getWalletData(blockchain: Blockchain?): WalletData? {
|
||||
if (blockchain == null) return null
|
||||
return walletsData.find {
|
||||
(it.currency as? Currency.Blockchain)?.blockchain == blockchain &&
|
||||
!it.currency.isCustomCurrency(store.state.globalState.scanResponse!!.card.derivationStyle)
|
||||
}
|
||||
}
|
||||
|
||||
fun getSelectedWalletData(): WalletData? {
|
||||
|
|
@ -114,36 +166,90 @@ data class WalletState(
|
|||
|
||||
if (walletData.currency is Currency.Blockchain) {
|
||||
return wallet.recentTransactions.toPendingTransactions(wallet.address).isEmpty() &&
|
||||
wallet.amounts.toSendableAmounts().isEmpty()
|
||||
wallet.amounts.toSendableAmounts().isEmpty()
|
||||
} else if (walletData.currency is Currency.Token) (
|
||||
return wallet.recentTransactions.toPendingTransactionsForToken(
|
||||
walletData.currency.token, wallet.address).isEmpty()
|
||||
&& wallet.amounts[AmountType.Token(token = walletData.currency.token)]
|
||||
?.isAboveZero() != true
|
||||
)
|
||||
return wallet.recentTransactions.toPendingTransactionsForToken(
|
||||
walletData.currency.token, wallet.address
|
||||
).isEmpty()
|
||||
&& wallet.amounts[AmountType.Token(token = walletData.currency.token)]
|
||||
?.isAboveZero() != true
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isPrimaryCurrency(walletData: WalletData): Boolean {
|
||||
return (walletData.currency is Currency.Blockchain &&
|
||||
walletData.currency.blockchain == store.state.walletState.primaryBlockchain)
|
||||
|| (walletData.currency is Currency.Token &&
|
||||
walletData.currency.token == store.state.walletState.primaryToken)
|
||||
walletData.currency.blockchain == store.state.walletState.primaryBlockchain)
|
||||
|| (walletData.currency is Currency.Token &&
|
||||
walletData.currency.token == store.state.walletState.primaryToken)
|
||||
}
|
||||
|
||||
fun replaceWalletInWallets(walletData: WalletData?): List<WalletData> {
|
||||
if (walletData == null) return walletsData
|
||||
fun replaceWalletInWallets(wallet: WalletStore?): List<WalletStore> {
|
||||
if (wallet == null) return wallets
|
||||
var changed = false
|
||||
val updatedWallets = walletsData.map {
|
||||
if (it.currency == walletData.currency) {
|
||||
val updatedWallets = wallets.map {
|
||||
if (it.blockchainNetwork == wallet.blockchainNetwork) {
|
||||
changed = true
|
||||
walletData
|
||||
wallet
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
return if (changed) updatedWallets else walletsData + walletData
|
||||
return if (changed) updatedWallets else wallets + wallet
|
||||
}
|
||||
|
||||
fun updateWalletData(walletData: WalletData?): WalletState {
|
||||
if (walletData == null) return this
|
||||
return updateWalletsData(listOf(walletData))
|
||||
}
|
||||
|
||||
fun updateWalletsData(
|
||||
walletsData: List<WalletData>
|
||||
): WalletState {
|
||||
|
||||
val walletStores = walletsData
|
||||
.map { BlockchainNetwork(it.currency.blockchain, it.currency.derivationPath, emptyList()) }
|
||||
.distinct().map { getWalletStore(it) }.mapNotNull { it?.updateWallets(walletsData) }
|
||||
|
||||
return updateWalletStores(walletStores)
|
||||
|
||||
}
|
||||
|
||||
fun updateWalletStore(walletStore: WalletStore?): WalletState {
|
||||
return copy(wallets = replaceWalletInWallets(walletStore))
|
||||
}
|
||||
|
||||
fun updateWalletStores(walletStores: List<WalletStore>): WalletState {
|
||||
val walletStores = walletStores.toMutableList()
|
||||
val updatedWallets = wallets.map { oldWalletStore ->
|
||||
val walletStore = walletStores.find { it.blockchainNetwork == oldWalletStore.blockchainNetwork }
|
||||
if (walletStore != null) {
|
||||
walletStores.remove(walletStore)
|
||||
walletStore
|
||||
} else {
|
||||
oldWalletStore
|
||||
}
|
||||
}
|
||||
return copy(wallets = updatedWallets + walletStores)
|
||||
}
|
||||
|
||||
fun removeWallet(walletData: WalletData?): WalletState {
|
||||
if (walletData == null) return this
|
||||
return if (walletData.currency is Currency.Blockchain) {
|
||||
val walletStores = wallets.filterNot {
|
||||
it.blockchainNetwork.blockchain == walletData.currency.blockchain
|
||||
&& it.blockchainNetwork.derivationPath == walletData.currency.derivationPath
|
||||
}
|
||||
copy(wallets = walletStores)
|
||||
} else {
|
||||
val walletStore = getWalletStore(walletData.currency)
|
||||
val walletDataList = walletStore?.walletsData
|
||||
?.filterNot { it.currency == walletData.currency }
|
||||
?: emptyList()
|
||||
val updatedWalletStore = walletStore?.copy(walletsData = walletDataList)
|
||||
updateWalletStore(updatedWalletStore)
|
||||
}
|
||||
}
|
||||
|
||||
fun replaceSomeWallets(newWallets: List<WalletData>): List<WalletData> {
|
||||
|
|
@ -165,20 +271,26 @@ data class WalletState(
|
|||
exchangeManager: CurrencyExchangeManager?,
|
||||
walletData: WalletData
|
||||
): WalletData {
|
||||
return walletData.copy(tradeCryptoState = TradeCryptoState.from(exchangeManager, walletData))
|
||||
return walletData.copy(
|
||||
tradeCryptoState = TradeCryptoState.from(
|
||||
exchangeManager,
|
||||
walletData
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun updateTradeCryptoState(
|
||||
exchangeManager: CurrencyExchangeManager?,
|
||||
walletDataList: List<WalletData>
|
||||
): List<WalletData> {
|
||||
return walletDataList.map { it.copy(tradeCryptoState = TradeCryptoState.from(exchangeManager, it)) }
|
||||
}
|
||||
|
||||
fun addWalletManagers(newWalletManagers: List<WalletManager>): WalletState {
|
||||
val updatedWalletManagers = this.walletManagers +
|
||||
newWalletManagers.filterNot { this.blockchains.contains(it.wallet.blockchain) }
|
||||
return copy(walletManagers = updatedWalletManagers)
|
||||
return walletDataList.map {
|
||||
it.copy(
|
||||
tradeCryptoState = TradeCryptoState.from(
|
||||
exchangeManager,
|
||||
it
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +336,8 @@ data class Artwork(
|
|||
const val TWIN_CARD_1 = "https://app.tangem.com/cards/card_tg085.png"
|
||||
const val TWIN_CARD_2 = "https://app.tangem.com/cards/card_tg086.png"
|
||||
|
||||
const val TEMP_CARDANO = "https://verify.tangem.com/card/artwork?artworkId=card_ru039&CID=CB19000000040976&publicKey=0416E29423A6CC77CD07CBA52873E8F6F894B1AFB18EB3688ACC2C8D8E5AC84B80B0BA1B17B85E578E47044CE96BCFF3FB4499FA4941CAD3C1EF300A492B5B9659"
|
||||
const val TEMP_CARDANO =
|
||||
"https://verify.tangem.com/card/artwork?artworkId=card_ru039&CID=CB19000000040976&publicKey=0416E29423A6CC77CD07CBA52873E8F6F894B1AFB18EB3688ACC2C8D8E5AC84B80B0BA1B17B85E578E47044CE96BCFF3FB4499FA4941CAD3C1EF300A492B5B9659"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,7 +346,10 @@ data class TradeCryptoState(
|
|||
val buyingAllowed: Boolean = false,
|
||||
) {
|
||||
companion object {
|
||||
fun from(exchangeManager: CurrencyExchangeManager?, walletData: WalletData): TradeCryptoState {
|
||||
fun from(
|
||||
exchangeManager: CurrencyExchangeManager?,
|
||||
walletData: WalletData
|
||||
): TradeCryptoState {
|
||||
val status = exchangeManager ?: return walletData.tradeCryptoState
|
||||
val currency = walletData.currency
|
||||
|
||||
|
|
@ -267,7 +383,9 @@ data class WalletData(
|
|||
|
||||
fun shouldEnableTokenSendButton(): Boolean = !blockchainAmountIsEmpty() || !tokenAmountIsEmpty()
|
||||
|
||||
private fun blockchainAmountIsEmpty(): Boolean = currencyData.blockchainAmount?.isZero() ?: false
|
||||
private fun blockchainAmountIsEmpty(): Boolean =
|
||||
currencyData.blockchainAmount?.isZero() ?: false
|
||||
|
||||
private fun tokenAmountIsEmpty(): Boolean = currencyData.amount?.isZero() == true
|
||||
}
|
||||
|
||||
|
|
@ -280,17 +398,83 @@ sealed interface Currency {
|
|||
|
||||
val blockchain: com.tangem.blockchain.common.Blockchain
|
||||
val currencySymbol: CryptoCurrencyName
|
||||
val derivationPath: String?
|
||||
|
||||
data class Token(
|
||||
val token: com.tangem.blockchain.common.Token
|
||||
val token: com.tangem.blockchain.common.Token,
|
||||
override val blockchain: com.tangem.blockchain.common.Blockchain,
|
||||
override val derivationPath: String?
|
||||
) : Currency {
|
||||
override val blockchain = token.blockchain
|
||||
override val currencySymbol: CryptoCurrencyName = token.symbol
|
||||
override val currencySymbol = token.symbol
|
||||
}
|
||||
|
||||
data class Blockchain(
|
||||
override val blockchain: com.tangem.blockchain.common.Blockchain
|
||||
override val blockchain: com.tangem.blockchain.common.Blockchain,
|
||||
override val derivationPath: String?
|
||||
) : Currency {
|
||||
override val currencySymbol: CryptoCurrencyName = blockchain.currency
|
||||
}
|
||||
|
||||
fun isCustomCurrency(derivationStyle: DerivationStyle?): Boolean {
|
||||
if (derivationPath == null || derivationStyle == null) return false
|
||||
return derivationPath != blockchain.derivationPath(derivationStyle)?.rawPath
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun fromBlockchainNetwork(
|
||||
blockchainNetwork: BlockchainNetwork,
|
||||
token: com.tangem.blockchain.common.Token? = null
|
||||
): Currency {
|
||||
return if (token != null) {
|
||||
Currency.Token(
|
||||
token = token,
|
||||
blockchain = blockchainNetwork.blockchain,
|
||||
derivationPath = blockchainNetwork.derivationPath
|
||||
)
|
||||
} else {
|
||||
Currency.Blockchain(
|
||||
blockchain = blockchainNetwork.blockchain,
|
||||
derivationPath = blockchainNetwork.derivationPath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class WalletStore(
|
||||
val walletManager: WalletManager?,
|
||||
val blockchainNetwork: BlockchainNetwork,
|
||||
val walletsData: List<WalletData>
|
||||
) {
|
||||
fun updateWallets(walletDataList: List<WalletData>): WalletStore {
|
||||
val relevantWalletDataList = walletDataList.filter {
|
||||
it.currency.blockchain == blockchainNetwork.blockchain &&
|
||||
it.currency.derivationPath == blockchainNetwork.derivationPath
|
||||
}.toMutableList()
|
||||
val updatedWalletDataList = walletsData.map { walletData ->
|
||||
val matchingWalletData = relevantWalletDataList.find { it.currency == walletData.currency }
|
||||
if (matchingWalletData != null) relevantWalletDataList.remove(matchingWalletData)
|
||||
matchingWalletData ?: walletData
|
||||
}
|
||||
return copy(walletsData = updatedWalletDataList + relevantWalletDataList)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as WalletStore
|
||||
|
||||
if (walletManager != other.walletManager) return false
|
||||
if (blockchainNetwork != other.blockchainNetwork) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = walletManager?.hashCode() ?: 0
|
||||
result = 31 * result + blockchainNetwork.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.currenciesRepository
|
||||
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
|
||||
import com.tangem.tap.domain.extensions.makeWalletManagersForApp
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
|
|
@ -31,11 +32,8 @@ class MultiWalletMiddleware {
|
|||
val tapWalletManager = globalState.tapWalletManager
|
||||
|
||||
when (action) {
|
||||
is WalletAction.MultiWallet.AddWalletManagers -> {
|
||||
globalState.feedbackManager?.infoHolder?.setWalletsInfo(action.walletManagers)
|
||||
if (globalState.scanResponse?.isDemoCard() == true) {
|
||||
addDummyBalances(action.walletManagers)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddBlockchains -> {
|
||||
handleAddingWalletManagers(globalState, action.walletManagers)
|
||||
}
|
||||
is WalletAction.MultiWallet.SelectWallet -> {
|
||||
if (action.walletData != null) {
|
||||
|
|
@ -43,33 +41,39 @@ class MultiWalletMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletAction.MultiWallet.AddToken -> {
|
||||
globalState.scanResponse?.card?.cardId?.let {
|
||||
currenciesRepository.saveAddedToken(it, action.token)
|
||||
}
|
||||
addTokens(listOf(action.token), walletState, globalState)
|
||||
addTokens(listOf(action.token), action.blockchain, walletState, globalState)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddTokens -> {
|
||||
addTokens(action.tokens, walletState, globalState)
|
||||
addTokens(action.tokens, action.blockchain, walletState, globalState)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddBlockchain -> {
|
||||
globalState.scanResponse?.let {
|
||||
currenciesRepository.saveAddedBlockchain(it.card.cardId, action.blockchain)
|
||||
if (walletState?.blockchains?.contains(action.blockchain) != true) {
|
||||
tapWalletManager.walletManagerFactory
|
||||
.makeWalletManagerForApp(it, action.blockchain)?.let { walletManager ->
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
|
||||
}
|
||||
}
|
||||
action.walletManager?.let {
|
||||
handleAddingWalletManagers(globalState, listOf(action.walletManager))
|
||||
}
|
||||
store.dispatch(WalletAction.LoadFiatRate(
|
||||
currencyList = listOf(Currency.Blockchain(action.blockchain)))
|
||||
|
||||
globalState.scanResponse?.let {
|
||||
currenciesRepository.saveUpdatedCurrency(
|
||||
cardId = it.card.cardId,
|
||||
blockchainNetwork = action.blockchain
|
||||
)
|
||||
}
|
||||
store.dispatch(
|
||||
WalletAction.LoadFiatRate(
|
||||
currencyList = listOf(
|
||||
Currency.Blockchain(
|
||||
action.blockchain.blockchain,
|
||||
action.blockchain.derivationPath
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
store.dispatch(WalletAction.LoadWallet(action.blockchain)
|
||||
store.dispatch(
|
||||
WalletAction.LoadWallet(action.blockchain)
|
||||
)
|
||||
}
|
||||
is WalletAction.MultiWallet.SaveCurrencies -> {
|
||||
globalState.scanResponse?.card?.cardId?.let {
|
||||
currenciesRepository.saveCardCurrencies(it, action.cardCurrencies)
|
||||
currenciesRepository.saveCurrencies(it, action.blockchainNetworks)
|
||||
}
|
||||
}
|
||||
is WalletAction.MultiWallet.RemoveWallet -> {
|
||||
|
|
@ -77,13 +81,27 @@ class MultiWalletMiddleware {
|
|||
when (val currency = action.walletData.currency) {
|
||||
is Currency.Blockchain -> {
|
||||
cardId?.let {
|
||||
currenciesRepository.removeBlockchain(it, currency.blockchain)
|
||||
currenciesRepository.removeBlockchain(
|
||||
cardId = it,
|
||||
blockchainNetwork = BlockchainNetwork(
|
||||
currency.blockchain, currency.derivationPath,
|
||||
emptyList()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
is Currency.Token -> {
|
||||
walletState?.getWalletManager(currency.token)
|
||||
?.removeToken(currency.token)
|
||||
cardId?.let { currenciesRepository.removeToken(it, currency.token) }
|
||||
val walletManager = walletState?.getWalletManager(currency.token)
|
||||
if (walletManager != null) {
|
||||
walletManager.removeToken(currency.token)
|
||||
cardId?.let {
|
||||
currenciesRepository.removeToken(
|
||||
cardId = it,
|
||||
token = currency.token,
|
||||
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -94,8 +112,12 @@ class MultiWalletMiddleware {
|
|||
val cardFirmware = scanResponse.card.firmwareVersion
|
||||
val blockchains = currenciesRepository.getBlockchains(cardFirmware)
|
||||
.filterNot { walletState?.blockchains?.contains(it) == true }
|
||||
.map { BlockchainNetwork(it, null, emptyList()) }
|
||||
val walletManagers =
|
||||
tapWalletManager.walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains)
|
||||
tapWalletManager.walletManagerFactory.makeWalletManagersForApp(
|
||||
scanResponse,
|
||||
blockchains
|
||||
)
|
||||
|
||||
scope.launch {
|
||||
walletManagers.map { walletManager ->
|
||||
|
|
@ -105,18 +127,19 @@ class MultiWalletMiddleware {
|
|||
val coinAmount = wallet.amounts[AmountType.Coin]?.value
|
||||
if (coinAmount != null && !coinAmount.isZero()) {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
if (walletState?.getWalletData(wallet.blockchain) == null) {
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.AddWalletManagers(
|
||||
listOfNotNull(walletManager)
|
||||
)
|
||||
)
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
if (walletState?.getWalletData(blockchainNetwork) == null) {
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.AddBlockchain(
|
||||
wallet.blockchain
|
||||
blockchainNetwork, walletManager
|
||||
)
|
||||
)
|
||||
store.dispatch(
|
||||
WalletAction.LoadWallet.Success(
|
||||
wallet = wallet,
|
||||
blockchain = blockchainNetwork
|
||||
)
|
||||
)
|
||||
store.dispatch(WalletAction.LoadWallet.Success(wallet))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -130,30 +153,42 @@ class MultiWalletMiddleware {
|
|||
|
||||
val walletFactory = tapWalletManager.walletManagerFactory
|
||||
val card = scanResponse.card
|
||||
val walletManager = walletState?.getWalletManager(Blockchain.Ethereum)
|
||||
?: walletFactory.makeWalletManagerForApp(scanResponse, Blockchain.Ethereum)
|
||||
|
||||
val walletManager = walletState?.getWalletManager(
|
||||
Currency.Blockchain(Blockchain.Ethereum, null)
|
||||
)
|
||||
?: walletFactory.makeWalletManagerForApp(
|
||||
scanResponse,
|
||||
Currency.Blockchain(Blockchain.Ethereum, null)
|
||||
)
|
||||
|
||||
val tokenFinder = walletManager as? TokenFinder ?: return
|
||||
scope.launch {
|
||||
val result = tokenFinder.findTokens()
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
if (result.data.isNotEmpty()) {
|
||||
currenciesRepository.saveAddedTokens(card.cardId, result.data)
|
||||
val blockchainNetwork = BlockchainNetwork(
|
||||
walletManager.wallet.blockchain,
|
||||
walletManager.wallet.publicKey.derivationPath?.rawPath,
|
||||
walletManager.cardTokens.toList()
|
||||
)
|
||||
currenciesRepository.saveUpdatedCurrency(
|
||||
card.cardId,
|
||||
blockchainNetwork
|
||||
)
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.AddWalletManagers(
|
||||
WalletAction.MultiWallet.AddBlockchain(
|
||||
blockchainNetwork,
|
||||
walletManager
|
||||
)
|
||||
)
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.AddBlockchain(
|
||||
walletManager.wallet.blockchain
|
||||
)
|
||||
)
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.AddTokens(
|
||||
walletManager.cardTokens.toList()
|
||||
walletManager.cardTokens.toList(),
|
||||
blockchainNetwork
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -173,38 +208,60 @@ class MultiWalletMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun addTokens(tokens: List<Token>, walletState: WalletState?, globalState: GlobalState?) {
|
||||
private fun handleAddingWalletManagers(
|
||||
globalState: GlobalState,
|
||||
walletManagers: List<WalletManager>
|
||||
) {
|
||||
globalState.feedbackManager?.infoHolder?.setWalletsInfo(walletManagers)
|
||||
if (globalState.scanResponse?.isDemoCard() == true) {
|
||||
addDummyBalances(walletManagers)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTokens(
|
||||
tokens: List<Token>, blockchainNetwork: BlockchainNetwork,
|
||||
walletState: WalletState?, globalState: GlobalState?
|
||||
) {
|
||||
val scanResponse = globalState?.scanResponse ?: return
|
||||
val wmFactory = globalState.tapWalletManager.walletManagerFactory
|
||||
|
||||
val groupedTokens = tokens.groupBy { it.blockchain }
|
||||
val walletManagers = groupedTokens.mapNotNull { entry ->
|
||||
val blockchain = entry.key
|
||||
val tokensList = entry.value
|
||||
val walletManager = walletState?.getWalletManager(blockchain)
|
||||
?: wmFactory.makeWalletManagerForApp(scanResponse, blockchain)?.also {
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(it))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchain))
|
||||
}
|
||||
store.dispatch(WalletAction.LoadFiatRate(currencyList = tokensList.map { Currency.Token(it) }))
|
||||
walletManager?.apply { addTokens(tokensList) }
|
||||
}
|
||||
val walletManager = walletState?.getWalletManager(blockchainNetwork)
|
||||
?: wmFactory.makeWalletManagerForApp(scanResponse, blockchainNetwork)?.also {
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchainNetwork, it))
|
||||
} ?: return
|
||||
|
||||
store.dispatch(WalletAction.LoadFiatRate(currencyList = tokens.map { token ->
|
||||
Currency.Token(
|
||||
token, blockchainNetwork.blockchain, blockchainNetwork.derivationPath
|
||||
)
|
||||
}))
|
||||
walletManager.addTokens(tokens)
|
||||
currenciesRepository.saveUpdatedCurrency(
|
||||
cardId = scanResponse.card.cardId,
|
||||
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
)
|
||||
scope.launch {
|
||||
walletManagers.forEach { walletManager ->
|
||||
when (val result = walletManager.safeUpdate()) {
|
||||
is com.tangem.common.services.Result.Success -> {
|
||||
val wallet = result.data
|
||||
wallet.getTokens()
|
||||
.filter { tokens.contains(it) }
|
||||
.mapNotNull { token -> wallet.getTokenAmount(token)?.let { Pair(token, it) } }
|
||||
.forEach {
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(WalletAction.MultiWallet.TokenLoaded(it.second, it.first))
|
||||
}
|
||||
when (val result = walletManager.safeUpdate()) {
|
||||
is com.tangem.common.services.Result.Success -> {
|
||||
val wallet = result.data
|
||||
wallet.getTokens()
|
||||
.filter { tokens.contains(it) }
|
||||
.mapNotNull { token ->
|
||||
wallet.getTokenAmount(token)?.let { Pair(token, it) }
|
||||
}
|
||||
.forEach {
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.TokenLoaded(
|
||||
it.second,
|
||||
it.first,
|
||||
blockchainNetwork
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
is com.tangem.common.services.Result.Failure -> {}
|
||||
}
|
||||
}
|
||||
is com.tangem.common.services.Result.Failure -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import android.net.Uri
|
|||
import androidx.core.content.ContextCompat
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.isZero
|
||||
|
|
@ -25,10 +23,7 @@ import com.tangem.tap.domain.extensions.toSendableAmounts
|
|||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.network.NetworkStateChanged
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -63,7 +58,11 @@ class WalletMiddleware {
|
|||
when (action) {
|
||||
is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(state, action)
|
||||
is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState)
|
||||
is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState, globalState)
|
||||
is WalletAction.MultiWallet -> multiWalletMiddleware.handle(
|
||||
action,
|
||||
walletState,
|
||||
globalState
|
||||
)
|
||||
is WalletAction.LoadWallet -> {
|
||||
scope.launch {
|
||||
if (action.blockchain == null) {
|
||||
|
|
@ -79,9 +78,19 @@ class WalletMiddleware {
|
|||
is WalletAction.LoadWallet.Success -> {
|
||||
val coinAmount = action.wallet.amounts[AmountType.Coin]?.value
|
||||
if (coinAmount != null && !coinAmount.isZero()) {
|
||||
if (walletState.getWalletData(action.wallet.blockchain) == null) {
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(action.wallet.blockchain))
|
||||
store.dispatch(WalletAction.LoadWallet.Success(action.wallet))
|
||||
if (walletState.getWalletData(action.blockchain) == null) {
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.AddBlockchain(
|
||||
action.blockchain,
|
||||
null
|
||||
)
|
||||
)
|
||||
store.dispatch(
|
||||
WalletAction.LoadWallet.Success(
|
||||
action.wallet,
|
||||
action.blockchain
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
|
||||
|
|
@ -121,7 +130,8 @@ class WalletMiddleware {
|
|||
)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val scanNoteResponse = globalState.scanResponse?.copy(card = result.data)
|
||||
val scanNoteResponse =
|
||||
globalState.scanResponse?.copy(card = result.data)
|
||||
scanNoteResponse?.let { store.onCardScanned(scanNoteResponse) }
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
|
|
@ -197,8 +207,10 @@ class WalletMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletAction.ShowDialog.QrCode -> {
|
||||
val selectedWalletData = walletState.getWalletData(walletState.selectedCurrency) ?: return
|
||||
val selectedAddressData = selectedWalletData.walletAddresses?.selectedAddress ?: return
|
||||
val selectedWalletData =
|
||||
walletState.getWalletData(walletState.selectedCurrency) ?: return
|
||||
val selectedAddressData =
|
||||
selectedWalletData.walletAddresses?.selectedAddress ?: return
|
||||
|
||||
val currency = selectedWalletData.currency
|
||||
store.dispatchDialogShow(AppDialog.AddressInfoDialog(currency, selectedAddressData))
|
||||
|
|
@ -209,28 +221,38 @@ class WalletMiddleware {
|
|||
private fun prepareSendAction(amount: Amount?, state: WalletState?): Action {
|
||||
val selectedWalletData = state?.getSelectedWalletData()
|
||||
val currency = selectedWalletData?.currency
|
||||
val walletManager = state?.getWalletManager(currency)
|
||||
val wallet = walletManager?.wallet
|
||||
val walletStore = state?.getWalletStore(currency)
|
||||
|
||||
return if (amount != null) {
|
||||
if (amount.type is AmountType.Token) {
|
||||
prepareSendActionForToken(amount, state, selectedWalletData, wallet, walletManager)
|
||||
prepareSendActionForToken(amount, state, selectedWalletData, walletStore)
|
||||
} else {
|
||||
PrepareSendScreen(amount, selectedWalletData?.fiatRate, walletManager)
|
||||
PrepareSendScreen(amount, selectedWalletData?.fiatRate, walletStore?.walletManager)
|
||||
}
|
||||
} else {
|
||||
val amounts = wallet?.amounts?.toSendableAmounts()
|
||||
val amounts = walletStore?.walletManager?.wallet?.amounts?.toSendableAmounts()
|
||||
if (currency != null && state.isMultiwalletAllowed) {
|
||||
when (currency) {
|
||||
is Currency.Blockchain -> {
|
||||
val amountToSend = amounts?.find { it.currencySymbol == currency.blockchain.currency }
|
||||
?: return WalletAction.Send.ChooseCurrency(amounts)
|
||||
PrepareSendScreen(amountToSend, selectedWalletData.fiatRate, walletManager)
|
||||
val amountToSend =
|
||||
amounts?.find { it.currencySymbol == currency.blockchain.currency }
|
||||
?: return WalletAction.Send.ChooseCurrency(amounts)
|
||||
PrepareSendScreen(
|
||||
coinAmount = amountToSend,
|
||||
coinRate = selectedWalletData.fiatRate,
|
||||
walletManager = walletStore.walletManager
|
||||
)
|
||||
}
|
||||
is Currency.Token -> {
|
||||
val amountToSend = amounts?.find { it.currencySymbol == currency.token.symbol }
|
||||
?: return WalletAction.Send.ChooseCurrency(amounts)
|
||||
prepareSendActionForToken(amountToSend, state, selectedWalletData, wallet, walletManager)
|
||||
val amountToSend =
|
||||
amounts?.find { it.currencySymbol == currency.token.symbol }
|
||||
?: return WalletAction.Send.ChooseCurrency(amounts)
|
||||
prepareSendActionForToken(
|
||||
amount = amountToSend,
|
||||
state = state,
|
||||
selectedWalletData = selectedWalletData,
|
||||
walletStore = walletStore
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -238,24 +260,36 @@ class WalletMiddleware {
|
|||
WalletAction.Send.ChooseCurrency(amounts)
|
||||
} else {
|
||||
val amountToSend = amounts?.first()
|
||||
PrepareSendScreen(amountToSend, selectedWalletData?.fiatRate, walletManager)
|
||||
PrepareSendScreen(
|
||||
coinAmount = amountToSend,
|
||||
coinRate = selectedWalletData?.fiatRate,
|
||||
walletManager = walletStore?.walletManager
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareSendActionForToken(
|
||||
amount: Amount, state: WalletState?, selectedWalletData: WalletData?, wallet: Wallet?,
|
||||
walletManager: WalletManager?
|
||||
amount: Amount,
|
||||
state: WalletState?,
|
||||
selectedWalletData: WalletData?,
|
||||
walletStore: WalletStore?
|
||||
): PrepareSendScreen {
|
||||
val coinRate = state?.getWalletData(wallet?.blockchain)?.fiatRate
|
||||
val coinRate = state?.getWalletData(walletStore?.blockchainNetwork)?.fiatRate
|
||||
val tokenRate = if (state?.isMultiwalletAllowed == true) {
|
||||
selectedWalletData?.fiatRate
|
||||
} else {
|
||||
selectedWalletData?.currencyData?.token?.fiatRate
|
||||
}
|
||||
val coinAmount = walletStore?.walletManager?.wallet?.amounts?.get(AmountType.Coin)
|
||||
|
||||
return PrepareSendScreen(
|
||||
wallet?.amounts?.get(AmountType.Coin), coinRate, walletManager,
|
||||
amount, tokenRate)
|
||||
coinAmount = coinAmount,
|
||||
coinRate = coinRate,
|
||||
walletManager = walletStore?.walletManager,
|
||||
tokenAmount = amount,
|
||||
tokenRate = tokenRate
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.extensions.isZero
|
|||
import com.tangem.tap.common.extensions.toFiatString
|
||||
import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
|
||||
import com.tangem.tap.features.wallet.models.toPendingTransactions
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
|
|
@ -17,66 +18,85 @@ import com.tangem.tap.store
|
|||
class MultiWalletReducer {
|
||||
fun reduce(action: WalletAction.MultiWallet, state: WalletState): WalletState {
|
||||
return when (action) {
|
||||
is WalletAction.MultiWallet.AddWalletManagers -> {
|
||||
state.addWalletManagers(action.walletManagers)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddBlockchains -> {
|
||||
val walletsData = action.blockchains.map { blockchain ->
|
||||
val wallet = state.getWalletManager(blockchain)?.wallet
|
||||
val wallets: List<WalletStore> = action.blockchains.map { blockchain ->
|
||||
val walletManager = action.walletManagers.first {
|
||||
it.wallet.blockchain == blockchain.blockchain &&
|
||||
(it.wallet.publicKey.derivationPath?.rawPath == blockchain.derivationPath)
|
||||
}
|
||||
val wallet = walletManager.wallet
|
||||
val cardToken = if (!state.isMultiwalletAllowed) {
|
||||
wallet?.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) }
|
||||
wallet.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
WalletData(
|
||||
val walletData = WalletData(
|
||||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.Loading,
|
||||
blockchain.fullName,
|
||||
currencySymbol = blockchain.currency,
|
||||
blockchain.blockchain.fullName,
|
||||
currencySymbol = blockchain.blockchain.currency,
|
||||
token = cardToken
|
||||
),
|
||||
walletAddresses = createAddressList(wallet),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
currency = Currency.Blockchain(blockchain),
|
||||
currency = Currency.Blockchain(
|
||||
blockchain.blockchain,
|
||||
blockchain.derivationPath
|
||||
),
|
||||
)
|
||||
|
||||
WalletStore(
|
||||
walletManager = walletManager,
|
||||
blockchainNetwork = blockchain,
|
||||
walletsData = listOf(walletData)
|
||||
)
|
||||
}
|
||||
|
||||
val selectedCurrency = if (!state.isMultiwalletAllowed) {
|
||||
walletsData[0].currency
|
||||
wallets[0].walletsData[0].currency
|
||||
} else {
|
||||
state.selectedCurrency
|
||||
}
|
||||
state.copy(
|
||||
walletsData = walletsData,
|
||||
wallets = wallets,
|
||||
selectedCurrency = selectedCurrency
|
||||
)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddBlockchain -> {
|
||||
val wallet = state.getWalletManager(action.blockchain)?.wallet
|
||||
val walletManager = action.walletManager ?: state.getWalletManager(action.blockchain)
|
||||
val wallet = walletManager?.wallet
|
||||
|
||||
val walletData = WalletData(
|
||||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.Loading,
|
||||
action.blockchain.fullName,
|
||||
currencySymbol = action.blockchain.currency,
|
||||
action.blockchain.blockchain.fullName,
|
||||
currencySymbol = action.blockchain.blockchain.currency,
|
||||
),
|
||||
walletAddresses = createAddressList(wallet),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
currency = Currency.Blockchain(action.blockchain),
|
||||
currency = Currency.Blockchain(
|
||||
action.blockchain.blockchain,
|
||||
action.blockchain.derivationPath
|
||||
),
|
||||
)
|
||||
val newState = state.copy(walletsData = state.replaceWalletInWallets(walletData))
|
||||
val walletStore = WalletStore(
|
||||
walletManager = walletManager,
|
||||
blockchainNetwork = action.blockchain,
|
||||
walletsData = listOf(walletData)
|
||||
)
|
||||
|
||||
val newState = state.updateWalletStore(walletStore)
|
||||
if (wallet != null && wallet.amounts[AmountType.Coin]?.value != null) {
|
||||
OnWalletLoadedReducer().reduce(wallet, newState)
|
||||
OnWalletLoadedReducer().reduce(wallet, action.blockchain, newState)
|
||||
} else {
|
||||
newState
|
||||
}
|
||||
}
|
||||
is WalletAction.MultiWallet.AddTokens -> {
|
||||
val wallets = action.tokens.mapNotNull { token -> token.toWallet(state) }
|
||||
state.copy(walletsData = state.replaceSomeWallets(wallets))
|
||||
addTokens(action.tokens, action.blockchain, state)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddToken -> {
|
||||
val walletData = action.token.toWallet(state) ?: return state
|
||||
state.copy(walletsData = state.replaceWalletInWallets(walletData))
|
||||
addTokens(listOf(action.token), action.blockchain, state)
|
||||
}
|
||||
is WalletAction.MultiWallet.TokenLoaded -> {
|
||||
val pendingTransactions = state.getWalletManager(action.token)
|
||||
|
|
@ -84,7 +104,8 @@ class MultiWalletReducer {
|
|||
wallet.recentTransactions.toPendingTransactions(wallet.address)
|
||||
} ?: emptyList()
|
||||
|
||||
val sendButtonEnabled = action.amount.value?.isZero() == false && pendingTransactions.isEmpty()
|
||||
val sendButtonEnabled =
|
||||
action.amount.value?.isZero() == false && pendingTransactions.isEmpty()
|
||||
val tokenPendingTransactions = pendingTransactions
|
||||
.filter { it.currency == action.amount.currencySymbol }
|
||||
val tokenBalanceStatus = when {
|
||||
|
|
@ -107,10 +128,13 @@ class MultiWalletReducer {
|
|||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
|
||||
currency = Currency.Token(action.token)
|
||||
currency = Currency.Token(
|
||||
token = action.token,
|
||||
blockchain = action.blockchain.blockchain,
|
||||
derivationPath = action.blockchain.derivationPath
|
||||
)
|
||||
)
|
||||
val wallets = state.replaceWalletInWallets(newTokenWalletData)
|
||||
state.copy(walletsData = wallets)
|
||||
state.updateWalletData(newTokenWalletData)
|
||||
}
|
||||
is WalletAction.MultiWallet.SetIsMultiwalletAllowed ->
|
||||
state.copy(isMultiwalletAllowed = action.isMultiwalletAllowed)
|
||||
|
|
@ -119,21 +143,7 @@ class MultiWalletReducer {
|
|||
state.copy(selectedCurrency = action.walletData?.currency)
|
||||
|
||||
is WalletAction.MultiWallet.RemoveWallet -> {
|
||||
val wallets = state.walletsData.filterNot {
|
||||
it.currency == action.walletData.currency
|
||||
}
|
||||
if (action.walletData.currency is Currency.Blockchain) {
|
||||
state.copy(
|
||||
walletsData = wallets,
|
||||
walletManagers = state.walletManagers.filterNot {
|
||||
it.wallet.blockchain == action.walletData.currency.blockchain
|
||||
}
|
||||
)
|
||||
} else {
|
||||
state.copy(walletsData = wallets)
|
||||
}
|
||||
|
||||
|
||||
state.removeWallet(action.walletData)
|
||||
}
|
||||
is WalletAction.MultiWallet.SetPrimaryBlockchain ->
|
||||
state.copy(primaryBlockchain = action.blockchain)
|
||||
|
|
@ -147,7 +157,14 @@ class MultiWalletReducer {
|
|||
}
|
||||
}
|
||||
|
||||
fun Token.toWallet(state: WalletState): WalletData? {
|
||||
private fun addTokens(
|
||||
tokens: List<Token>, blockchain: BlockchainNetwork, state: WalletState
|
||||
): WalletState {
|
||||
val wallets = tokens.mapNotNull { token -> token.toWallet(state, blockchain) }
|
||||
return state.updateWalletsData(wallets)
|
||||
}
|
||||
|
||||
fun Token.toWallet(state: WalletState, blockchain: BlockchainNetwork): WalletData? {
|
||||
if (!state.isMultiwalletAllowed) return null
|
||||
if (state.currencies.any { it is Currency.Token && it.token == this }) {
|
||||
return null
|
||||
|
|
@ -164,6 +181,6 @@ fun Token.toWallet(state: WalletState): WalletData? {
|
|||
),
|
||||
walletAddresses = walletAddresses,
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
currency = Currency.Token(this),
|
||||
currency = Currency.fromBlockchainNetwork(blockchain, this)
|
||||
)
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.tap.common.extensions.toFiatValue
|
|||
import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
||||
import com.tangem.tap.common.extensions.toFormattedFiatValue
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
|
||||
import com.tangem.tap.features.wallet.models.toPendingTransactions
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
|
|
@ -19,25 +20,30 @@ import java.math.RoundingMode
|
|||
|
||||
class OnWalletLoadedReducer {
|
||||
|
||||
fun reduce(wallet: Wallet, walletState: WalletState): WalletState {
|
||||
fun reduce(wallet: Wallet, blockchainNetwork: BlockchainNetwork, walletState: WalletState): WalletState {
|
||||
return if (!walletState.isMultiwalletAllowed) {
|
||||
onSingleWalletLoaded(wallet, walletState)
|
||||
} else {
|
||||
onMultiWalletLoaded(wallet, walletState)
|
||||
onMultiWalletLoaded(wallet, blockchainNetwork, walletState)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onMultiWalletLoaded(wallet: Wallet, walletState: WalletState): WalletState {
|
||||
private fun onMultiWalletLoaded(
|
||||
wallet: Wallet,
|
||||
blockchainNetwork: BlockchainNetwork,
|
||||
walletState: WalletState
|
||||
): WalletState {
|
||||
val fiatCurrencySymbol = store.state.globalState.appCurrency
|
||||
val exchangeManager = store.state.globalState.currencyExchangeManager
|
||||
|
||||
val coinAmountValue = wallet.amounts[AmountType.Coin]?.value
|
||||
if (walletState.getWalletData(wallet.blockchain) == null) {
|
||||
if (walletState.getWalletData(blockchainNetwork) == null) {
|
||||
return walletState
|
||||
}
|
||||
val formattedAmount = coinAmountValue?.toFormattedCurrencyString(
|
||||
wallet.blockchain.decimals(),
|
||||
wallet.blockchain.currency)
|
||||
wallet.blockchain.currency
|
||||
)
|
||||
|
||||
val pendingTransactions = wallet.recentTransactions
|
||||
.toPendingTransactions(wallet.address)
|
||||
|
|
@ -48,7 +54,7 @@ class OnWalletLoadedReducer {
|
|||
} else {
|
||||
BalanceStatus.VerifiedOnline
|
||||
}
|
||||
val walletData = walletState.getWalletData(wallet.blockchain)
|
||||
val walletData = walletState.getWalletData(blockchainNetwork)
|
||||
|
||||
val fiatAmount = walletData?.fiatRate?.let { coinAmountValue?.toFiatValue(it) }
|
||||
val newWalletData = walletData?.copy(
|
||||
|
|
@ -63,29 +69,34 @@ class OnWalletLoadedReducer {
|
|||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(coinSendButton),
|
||||
currency = Currency.Blockchain(wallet.blockchain),
|
||||
currency = Currency.fromBlockchainNetwork(blockchainNetwork),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletData),
|
||||
)
|
||||
|
||||
val tokens = wallet.getTokens().mapNotNull { token ->
|
||||
val tokenWalletData = walletState.getWalletData(token)
|
||||
val tokenPendingTransactions = pendingTransactions.filter { it.currency == token.symbol }
|
||||
val tokenPendingTransactions =
|
||||
pendingTransactions.filter { it.currency == token.symbol }
|
||||
val tokenBalanceStatus = when {
|
||||
tokenPendingTransactions.isNotEmpty() -> BalanceStatus.TransactionInProgress
|
||||
pendingTransactions.isNotEmpty() -> BalanceStatus.SameCurrencyTransactionInProgress
|
||||
else -> BalanceStatus.VerifiedOnline
|
||||
}
|
||||
val tokenAmountValue = wallet.getTokenAmount(token)?.value
|
||||
val tokenFiatAmount = tokenWalletData?.fiatRate?.let { rate -> tokenAmountValue?.toFiatValue(rate) }
|
||||
val tokenFiatAmount =
|
||||
tokenWalletData?.fiatRate?.let { rate -> tokenAmountValue?.toFiatValue(rate) }
|
||||
|
||||
val tokenSendButton = newWalletData?.shouldEnableTokenSendButton() == true
|
||||
&& tokenPendingTransactions.isEmpty()
|
||||
&& tokenPendingTransactions.isEmpty()
|
||||
tokenWalletData?.copy(
|
||||
currencyData = tokenWalletData.currencyData.copy(
|
||||
status = tokenBalanceStatus,
|
||||
blockchainAmount = coinAmountValue,
|
||||
amount = tokenAmountValue,
|
||||
amountFormatted = tokenAmountValue?.toFormattedCurrencyString(token.decimals, token.symbol),
|
||||
amountFormatted = tokenAmountValue?.toFormattedCurrencyString(
|
||||
token.decimals,
|
||||
token.symbol
|
||||
),
|
||||
fiatAmount = tokenFiatAmount,
|
||||
fiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrencySymbol)
|
||||
),
|
||||
|
|
@ -102,8 +113,9 @@ class OnWalletLoadedReducer {
|
|||
} else {
|
||||
ProgressState.Done
|
||||
}
|
||||
return walletState.copy(
|
||||
state = state, walletsData = wallets, error = null
|
||||
val newState = walletState.updateWalletsData(wallets)
|
||||
return newState.copy(
|
||||
state = state, error = null
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +130,8 @@ class OnWalletLoadedReducer {
|
|||
val tokenAmount = wallet.getTokenAmount(token)
|
||||
if (tokenAmount != null) {
|
||||
val tokenFiatRate = walletState.primaryWallet?.currencyData?.token?.fiatRate
|
||||
val tokenFiatAmount = tokenFiatRate?.let { tokenAmount.value?.toFiatString(it, fiatCurrencySymbol) }
|
||||
val tokenFiatAmount =
|
||||
tokenFiatRate?.let { tokenAmount.value?.toFiatString(it, fiatCurrencySymbol) }
|
||||
TokenData(
|
||||
tokenAmount.value?.toFormattedCurrencyString(
|
||||
token.decimals, token.symbol
|
||||
|
|
@ -134,7 +147,8 @@ class OnWalletLoadedReducer {
|
|||
val amount = wallet.amounts[AmountType.Coin]?.value
|
||||
val formattedAmount = amount?.toFormattedCurrencyString(
|
||||
wallet.blockchain.decimals(),
|
||||
wallet.blockchain.currency)
|
||||
wallet.blockchain.currency
|
||||
)
|
||||
val fiatRate = walletState.primaryWallet?.fiatRate
|
||||
val fiatAmountRaw = fiatRate?.multiply(amount)?.setScale(2, RoundingMode.DOWN)
|
||||
val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencySymbol) }
|
||||
|
|
@ -161,9 +175,11 @@ class OnWalletLoadedReducer {
|
|||
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletState.primaryWallet),
|
||||
)
|
||||
val wallets = walletData?.let { listOf(walletData) } ?: emptyList()
|
||||
return walletState.copy(
|
||||
state = ProgressState.Done, walletsData = wallets, error = null
|
||||
val wallets = listOfNotNull(walletData)
|
||||
val updatedStore = walletState.getWalletStore(walletData?.currency)?.updateWallets(wallets)
|
||||
|
||||
return walletState.updateWalletStore(updatedStore).copy(
|
||||
state = ProgressState.Done, error = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import com.tangem.tap.common.redux.global.FiatCurrencyName
|
|||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.getArtworkUrl
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
|
|
@ -46,11 +47,21 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
is WalletAction.EmptyWallet -> {
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Done,
|
||||
walletsData = listOf(
|
||||
WalletData(
|
||||
currencyData = BalanceWidgetData(BalanceStatus.EmptyCard),
|
||||
mainButton = WalletMainButton.CreateWalletButton(true),
|
||||
currency = Currency.Blockchain(Blockchain.Unknown)
|
||||
wallets = listOf(
|
||||
WalletStore(
|
||||
walletManager = null,
|
||||
blockchainNetwork = BlockchainNetwork(
|
||||
Blockchain.Unknown,
|
||||
null,
|
||||
emptyList()
|
||||
),
|
||||
walletsData = listOf(
|
||||
WalletData(
|
||||
currencyData = BalanceWidgetData(BalanceStatus.EmptyCard),
|
||||
mainButton = WalletMainButton.CreateWalletButton(true),
|
||||
currency = Currency.Blockchain(Blockchain.Unknown, null)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -58,27 +69,43 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
is WalletAction.LoadData.Failure -> {
|
||||
when (action.error) {
|
||||
is TapError.NoInternetConnection -> {
|
||||
val wallets = newState.walletsData
|
||||
val wallets = newState.wallets
|
||||
.map {
|
||||
it.copy(
|
||||
currencyData = it.currencyData.copy(
|
||||
status = BalanceStatus.Unreachable
|
||||
)
|
||||
walletsData = it.walletsData.map {
|
||||
it.copy(
|
||||
currencyData = it.currencyData.copy(
|
||||
status = BalanceStatus.Unreachable
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Error,
|
||||
error = ErrorType.NoInternetConnection,
|
||||
walletsData = wallets
|
||||
wallets = wallets
|
||||
)
|
||||
}
|
||||
is TapError.UnknownBlockchain -> {
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Done,
|
||||
walletsData = listOf(
|
||||
WalletData(
|
||||
currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain),
|
||||
currency = Currency.Blockchain(Blockchain.Unknown)
|
||||
wallets = listOf(
|
||||
WalletStore(
|
||||
walletManager = null,
|
||||
blockchainNetwork = BlockchainNetwork(
|
||||
Blockchain.Unknown,
|
||||
null,
|
||||
emptyList()
|
||||
),
|
||||
walletsData = listOf(
|
||||
WalletData(
|
||||
currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain),
|
||||
currency = Currency.Blockchain(Blockchain.Unknown, null)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -94,26 +121,37 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
}
|
||||
is WalletAction.LoadWallet -> {
|
||||
if (action.blockchain == null) {
|
||||
val wallets = newState.walletsData.map { wallet ->
|
||||
wallet.copy(
|
||||
currencyData = wallet.currencyData.copy(
|
||||
status = BalanceStatus.Loading,
|
||||
currency = wallet.currencyData.currency,
|
||||
currencySymbol = wallet.currencyData.currencySymbol,
|
||||
),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, wallet)
|
||||
val wallets = newState.wallets.map {
|
||||
it.copy(
|
||||
walletsData = it.walletsData.map { walletData ->
|
||||
walletData.copy(
|
||||
currencyData = walletData.currencyData.copy(
|
||||
status = BalanceStatus.Loading,
|
||||
currency = walletData.currencyData.currency,
|
||||
currencySymbol = walletData.currencyData.currencySymbol,
|
||||
),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
tradeCryptoState = TradeCryptoState.from(
|
||||
exchangeManager,
|
||||
walletData
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Loading,
|
||||
walletsData = wallets,
|
||||
wallets = wallets,
|
||||
)
|
||||
} else {
|
||||
val walletManager = newState.getWalletManager(action.blockchain) ?: return newState
|
||||
val blockchain = walletManager.wallet.blockchain
|
||||
val currencies = listOf(Currency.Blockchain(blockchain)) +
|
||||
walletManager.cardTokens.map { Currency.Token(it) }
|
||||
val currencies = listOf(Currency.fromBlockchainNetwork(action.blockchain)) +
|
||||
walletManager.cardTokens.map {
|
||||
Currency.fromBlockchainNetwork(
|
||||
action.blockchain,
|
||||
it
|
||||
)
|
||||
}
|
||||
val newWallets = newState.walletsData.filter { currencies.contains(it.currency) }
|
||||
.map { wallet ->
|
||||
wallet.copy(
|
||||
|
|
@ -126,38 +164,52 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
tradeCryptoState = TradeCryptoState.from(exchangeManager, wallet)
|
||||
)
|
||||
}
|
||||
val wallets = newState.replaceSomeWallets(newWallets)
|
||||
newState = newState.copy(walletsData = newState.updateTradeCryptoState(exchangeManager, wallets))
|
||||
val wallets = newState.updateTradeCryptoState(exchangeManager, newState.replaceSomeWallets(newWallets))
|
||||
val walletStore = newState.getWalletStore(action.blockchain)?.updateWallets(wallets)
|
||||
newState = newState.updateWalletStore(walletStore)
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadWallet.Success -> newState = onWalletLoadedReducer.reduce(action.wallet, newState)
|
||||
is WalletAction.LoadWallet.Success -> newState = onWalletLoadedReducer.reduce(
|
||||
wallet = action.wallet,
|
||||
blockchainNetwork = action.blockchain,
|
||||
walletState = newState
|
||||
)
|
||||
is WalletAction.LoadWallet.NoAccount -> {
|
||||
val walletData = newState.getWalletData(action.wallet.blockchain)?.copy(
|
||||
val walletData = newState.getWalletData(action.blockchain)?.copy(
|
||||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.NoAccount, action.wallet.blockchain.fullName,
|
||||
currencySymbol = action.wallet.blockchain.currency,
|
||||
amountToCreateAccount = action.amountToCreateAccount
|
||||
)
|
||||
)
|
||||
val wallets = newState.replaceWalletInWallets(walletData)
|
||||
var updatedWalletStore = newState.getWalletStore(action.blockchain)
|
||||
?.updateWallets(listOfNotNull(walletData))
|
||||
|
||||
val progressState =
|
||||
if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
|
||||
if (updatedWalletStore?.walletsData?.any { it.currencyData.status == BalanceStatus.Loading } == true) {
|
||||
ProgressState.Loading
|
||||
} else {
|
||||
ProgressState.Done
|
||||
}
|
||||
newState = newState.copy(
|
||||
state = progressState,
|
||||
walletsData = newState.updateTradeCryptoState(exchangeManager, wallets)
|
||||
)
|
||||
updatedWalletStore =
|
||||
updatedWalletStore?.updateWallets(
|
||||
newState.updateTradeCryptoState(exchangeManager, updatedWalletStore.walletsData)
|
||||
)
|
||||
|
||||
newState = newState.updateWalletStore(updatedWalletStore)
|
||||
.copy(
|
||||
state = progressState
|
||||
)
|
||||
}
|
||||
|
||||
is WalletAction.LoadWallet.Failure -> {
|
||||
val message = if (newState.error == ErrorType.NoInternetConnection) {
|
||||
null
|
||||
} else {
|
||||
action.errorMessage
|
||||
}
|
||||
val walletData = newState.getWalletData(action.wallet.blockchain)
|
||||
val walletStore = newState.getWalletStore(action.wallet)
|
||||
val walletData = walletStore?.walletsData?.first { it.currency is Currency.Blockchain }
|
||||
val newWalletData = walletData?.copy(
|
||||
currencyData = walletData.currencyData.copy(
|
||||
status = BalanceStatus.Unreachable,
|
||||
|
|
@ -173,17 +225,23 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
)
|
||||
)
|
||||
}
|
||||
val wallets = newState.replaceSomeWallets(listOfNotNull(newWalletData) + tokenWallets)
|
||||
val updatedWallets =
|
||||
newState.updateTradeCryptoState(
|
||||
exchangeManager,
|
||||
walletStore!!.updateWallets(listOfNotNull(newWalletData) + tokenWallets).walletsData
|
||||
)
|
||||
|
||||
newState = newState.updateWalletsData(updatedWallets)
|
||||
|
||||
val progressState =
|
||||
if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
|
||||
if (newState.walletsData.any { it.currencyData.status == BalanceStatus.Loading }) {
|
||||
ProgressState.Loading
|
||||
} else {
|
||||
ProgressState.Done
|
||||
}
|
||||
|
||||
newState = newState.copy(
|
||||
state = progressState,
|
||||
walletsData = newState.updateTradeCryptoState(exchangeManager, wallets)
|
||||
)
|
||||
}
|
||||
is WalletAction.SetArtworkId -> {
|
||||
|
|
@ -230,25 +288,21 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
?: return newState
|
||||
val address = walletAddresses.list.firstOrNull { it.type == action.type }
|
||||
?: return newState
|
||||
val wallets = newState.replaceWalletInWallets(
|
||||
selectedWalletData?.copy(
|
||||
walletAddresses = WalletAddresses(
|
||||
address,
|
||||
walletAddresses.list
|
||||
)
|
||||
newState = newState.updateWalletData(selectedWalletData?.copy(
|
||||
walletAddresses = WalletAddresses(
|
||||
address,
|
||||
walletAddresses.list
|
||||
)
|
||||
)
|
||||
newState = newState.copy(walletsData = wallets)
|
||||
))
|
||||
}
|
||||
is WalletAction.SetWalletRent -> {
|
||||
var walletData = newState.getWalletData(action.blockchain)
|
||||
if (walletData == null) {
|
||||
newState
|
||||
} else {
|
||||
walletData = walletData.copy(warningRent = WalletRent(action.minRent, action.rentExempt))
|
||||
newState = newState.copy(
|
||||
walletsData = newState.replaceSomeWallets(listOf(walletData))
|
||||
)
|
||||
walletData =
|
||||
walletData.copy(warningRent = WalletRent(action.minRent, action.rentExempt))
|
||||
newState = newState.updateWalletsData(listOf(walletData))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -322,8 +376,11 @@ private fun setMultiWalletFiatRate(
|
|||
rate: BigDecimal, rateFormatted: String, currency: Currency,
|
||||
appCurrency: FiatCurrencyName, state: WalletState
|
||||
): WalletState {
|
||||
|
||||
val walletStore = state.getWalletStore(currency) ?: return state
|
||||
val wallet = walletStore.walletManager?.wallet
|
||||
val walletData = state.getWalletData(currency) ?: return state
|
||||
val wallet = state.getWalletManager(currency)?.wallet
|
||||
|
||||
val fiatAmount = when (currency) {
|
||||
is Currency.Blockchain ->
|
||||
wallet?.amounts?.get(AmountType.Coin)?.value?.toFiatValue(rate)
|
||||
|
|
@ -338,14 +395,14 @@ private fun setMultiWalletFiatRate(
|
|||
),
|
||||
fiatRate = rate, fiatRateString = rateFormatted
|
||||
)
|
||||
return state.copy(walletsData = state.replaceWalletInWallets(newWalletData))
|
||||
return state.updateWalletData(newWalletData)
|
||||
}
|
||||
|
||||
private fun setSingeWalletFiatRate(
|
||||
rate: BigDecimal, rateFormatted: String, currency: Currency,
|
||||
appCurrency: FiatCurrencyName, state: WalletState
|
||||
): WalletState {
|
||||
val wallet = state.walletManagers[0].wallet
|
||||
val wallet = state.primaryWalletManager?.wallet ?: return state
|
||||
val token = wallet.getFirstToken()
|
||||
|
||||
if (currency == state.primaryWallet?.currency) {
|
||||
|
|
@ -356,7 +413,7 @@ private fun setSingeWalletFiatRate(
|
|||
fiatRate = rate,
|
||||
fiatRateString = rateFormatted
|
||||
)
|
||||
return state.copy(walletsData = listOf(walletData))
|
||||
return state.updateWalletData(walletData)
|
||||
} else if (currency is Currency.Token && currency.token == token) {
|
||||
val tokenFiatAmount = wallet.getTokenAmount(token)?.value?.toFiatString(rate, appCurrency)
|
||||
val tokenData = state.primaryWallet?.currencyData?.token?.copy(
|
||||
|
|
@ -371,7 +428,7 @@ private fun setSingeWalletFiatRate(
|
|||
)
|
||||
)
|
||||
val wallets = walletData?.let { listOf(walletData) } ?: emptyList()
|
||||
return state.copy(walletsData = wallets)
|
||||
return state.updateWalletsData(wallets)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import com.tangem.tap.common.SnackbarHandler
|
|||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.onboarding.getQRReceiveMessage
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
|
|
@ -114,7 +115,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
|
||||
store.dispatch(
|
||||
WalletAction.LoadWallet(
|
||||
blockchain = selectedWallet.currency.blockchain
|
||||
blockchain = BlockchainNetwork(selectedWallet.currency.blockchain, selectedWallet.currency.derivationPath, emptyList())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -188,7 +189,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
|
||||
private fun showPendingTransactionsIfPresent(pendingTransactions: List<PendingTransaction>) {
|
||||
pendingTransactionAdapter.submitList(pendingTransactions)
|
||||
binding.rvPendingTransaction?.show(pendingTransactions.isNotEmpty())
|
||||
binding.rvPendingTransaction.show(pendingTransactions.isNotEmpty())
|
||||
}
|
||||
|
||||
private fun setupAddressCard(state: WalletData) = with(binding.lWalletDetails) {
|
||||
|
|
@ -212,7 +213,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
chipGroupAddressType.hide()
|
||||
}
|
||||
tvAddress.text = state.walletAddresses.selectedAddress.address
|
||||
tvExplore?.setOnClickListener {
|
||||
tvExplore.setOnClickListener {
|
||||
store.dispatch(
|
||||
WalletAction.ExploreAddress(
|
||||
state.walletAddresses.selectedAddress.exploreUrl,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.blockchain.common.Token
|
|||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.loadCurrenciesIcon
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.domain.TapWorkarounds.derivationStyle
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
|
|
@ -26,7 +27,11 @@ class WalletAdapter
|
|||
return currentList[position].currencyData.currencySymbol?.hashCode()?.toLong() ?: 0
|
||||
}
|
||||
|
||||
fun submitList(list: List<WalletData>, primaryBlockchain: Blockchain?, primaryToken: Token? = null) {
|
||||
fun submitList(
|
||||
list: List<WalletData>,
|
||||
primaryBlockchain: Blockchain?,
|
||||
primaryToken: Token? = null
|
||||
) {
|
||||
// We used this method to sort the list of currencies. Sorting is disabled for now.
|
||||
super.submitList(list)
|
||||
}
|
||||
|
|
@ -44,21 +49,22 @@ class WalletAdapter
|
|||
|
||||
object DiffUtilCallback : DiffUtil.ItemCallback<WalletData>() {
|
||||
override fun areContentsTheSame(
|
||||
oldItem: WalletData, newItem: WalletData
|
||||
oldItem: WalletData, newItem: WalletData
|
||||
) = oldItem == newItem
|
||||
|
||||
override fun areItemsTheSame(
|
||||
oldItem: WalletData, newItem: WalletData
|
||||
oldItem: WalletData, newItem: WalletData
|
||||
) = oldItem == newItem
|
||||
}
|
||||
|
||||
class WalletsViewHolder(val binding: ItemCurrencyWalletBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(wallet: WalletData) = with(binding) {
|
||||
tvCurrency.text = wallet.currencyData.currency
|
||||
tvAmount.text = wallet.currencyData.amountFormatted?.takeWhile { !it.isWhitespace() }
|
||||
tvCurrencySymbol.text = wallet.currencyData.amountFormatted?.takeLastWhile { !it.isWhitespace() }
|
||||
tvCurrencySymbol.text =
|
||||
wallet.currencyData.amountFormatted?.takeLastWhile { !it.isWhitespace() }
|
||||
tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted
|
||||
tvExchangeRate.text = wallet.fiatRateString
|
||||
cardWallet.setOnClickListener {
|
||||
|
|
@ -67,6 +73,11 @@ class WalletAdapter
|
|||
val blockchain = wallet.currency.blockchain
|
||||
val token = (wallet.currency as? Currency.Token)?.token
|
||||
|
||||
val isCustom =
|
||||
wallet.currency.isCustomCurrency(store.state.globalState.scanResponse?.card?.derivationStyle)
|
||||
tvExchangeRate.show(!isCustom)
|
||||
tvCustomCurrency.show(isCustom)
|
||||
|
||||
Picasso.get().loadCurrenciesIcon(
|
||||
imageView = ivCurrency,
|
||||
textView = tvTokenLetter,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import com.tangem.tap.common.extensions.show
|
|||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.currenciesRepository
|
||||
import com.tangem.tap.domain.TapWorkarounds.derivationStyle
|
||||
import com.tangem.tap.domain.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.features.tokens.redux.TokensAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletDialog
|
||||
|
|
@ -97,8 +100,20 @@ class MultiWalletView : WalletView {
|
|||
walletsAdapter.submitList(state.walletsData, state.primaryBlockchain, state.primaryToken)
|
||||
|
||||
binding.btnAddToken.setOnClickListener {
|
||||
store.dispatch(TokensAction.LoadCurrencies)
|
||||
store.dispatch(TokensAction.SetAddedCurrencies(state.walletsData))
|
||||
val card = store.state.globalState.scanResponse!!.card
|
||||
store.dispatch(TokensAction.LoadCurrencies(
|
||||
supportedBlockchains = currenciesRepository.getBlockchains(
|
||||
card.firmwareVersion,
|
||||
card.isTestCard
|
||||
)))
|
||||
store.dispatch(TokensAction.AllowToAddTokens(true))
|
||||
store.dispatch(TokensAction.SetAddedCurrencies(
|
||||
wallets = state.walletsData,
|
||||
derivationStyle = card.derivationStyle
|
||||
))
|
||||
store.dispatch(TokensAction.SetNonRemovableCurrencies(
|
||||
state.walletsData.filterNot { state.canBeRemoved(it) })
|
||||
)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))
|
||||
}
|
||||
handleErrorStates(state = state, binding = binding, fragment = fragment)
|
||||
|
|
|
|||
5
app/src/main/res/drawable/ic_arrow_collapsed.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_collapsed.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:autoMirrored="true" android:height="10dp"
|
||||
android:viewportHeight="10" android:viewportWidth="17"
|
||||
android:width="17dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#CCCCCC" android:fillType="evenOdd" android:pathData="M0.2689,0.95C0.6458,0.5463 1.2786,0.5244 1.6823,0.9013L8.5,7.2644L15.3177,0.9013C15.7214,0.5244 16.3542,0.5463 16.7311,0.95C17.1079,1.3538 17.0861,1.9865 16.6823,2.3634L8.5,10.0002L0.3177,2.3634C-0.0861,1.9865 -0.1079,1.3538 0.2689,0.95Z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_arrow_extended.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_extended.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:autoMirrored="true" android:height="10dp"
|
||||
android:viewportHeight="10" android:viewportWidth="17"
|
||||
android:width="17dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#CCCCCC" android:fillType="evenOdd" android:pathData="M0.2689,9.6823C0.6458,10.0861 1.2786,10.1079 1.6823,9.731L8.5,3.3679L15.3177,9.731C15.7214,10.1079 16.3542,10.0861 16.7311,9.6823C17.1079,9.2786 17.0861,8.6458 16.6823,8.2689L8.5,0.6321L0.3177,8.2689C-0.0861,8.6458 -0.1079,9.2786 0.2689,9.6823Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_avalanche_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_avalanche_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#F1F1F1" android:fillType="evenOdd" android:pathData="M16,8C16,12.4183 12.4183,16 8,16C3.5817,16 0,12.4183 0,8C0,3.5817 3.5817,0 8,0C12.4183,0 16,3.5817 16,8ZM5.733,11.1836H4.1804C3.8542,11.1836 3.6931,11.1836 3.5948,11.1207C3.4887,11.0519 3.4238,10.9379 3.416,10.8121C3.4101,10.6962 3.4906,10.5547 3.6518,10.2717L7.4853,3.5146C7.6484,3.2277 7.7309,3.0842 7.8351,3.0312C7.9471,2.9742 8.0808,2.9742 8.1928,3.0312C8.297,3.0842 8.3795,3.2277 8.5426,3.5146L9.3307,4.8903L9.3347,4.8973C9.5109,5.2052 9.6002,5.3613 9.6392,5.5251C9.6825,5.7039 9.6825,5.8926 9.6392,6.0715C9.5999,6.2365 9.5115,6.3938 9.3327,6.7063L7.319,10.2658L7.3138,10.2749C7.1365,10.5853 7.0466,10.7426 6.922,10.8613C6.7864,10.991 6.6233,11.0853 6.4445,11.1384C6.2813,11.1836 6.0986,11.1836 5.733,11.1836ZM9.6538,11.1836H11.8785C12.2067,11.1836 12.3718,11.1836 12.4701,11.1188C12.5762,11.05 12.643,10.934 12.6489,10.8083C12.6546,10.6961 12.5758,10.5601 12.4213,10.2935C12.416,10.2845 12.4107,10.2752 12.4052,10.2658L11.2909,8.3595L11.2782,8.3381C11.1216,8.0733 11.0426,7.9395 10.941,7.8878C10.8291,7.8309 10.6973,7.8309 10.5853,7.8878C10.4831,7.9409 10.4006,8.0805 10.2375,8.3615L9.1271,10.2678L9.1233,10.2744C8.9607,10.555 8.8795,10.6952 8.8854,10.8102C8.8932,10.936 8.9581,11.0519 9.0642,11.1207C9.1605,11.1836 9.3256,11.1836 9.6538,11.1836Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_avalanche_round.xml
Normal file
6
app/src/main/res/drawable/ic_avalanche_round.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#ffffff" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#E74142" android:fillType="evenOdd" android:pathData="M16,8C16,12.4183 12.4183,16 8,16C3.5817,16 0,12.4183 0,8C0,3.5817 3.5817,0 8,0C12.4183,0 16,3.5817 16,8ZM5.733,11.1836H4.1804C3.8542,11.1836 3.6931,11.1836 3.5948,11.1207C3.4887,11.0519 3.4238,10.9379 3.416,10.8121C3.4101,10.6962 3.4906,10.5547 3.6518,10.2717L7.4853,3.5146C7.6484,3.2277 7.7309,3.0842 7.8351,3.0312C7.9471,2.9742 8.0808,2.9742 8.1928,3.0312C8.297,3.0842 8.3795,3.2277 8.5426,3.5146L9.3307,4.8903L9.3347,4.8973C9.5109,5.2052 9.6002,5.3613 9.6392,5.5251C9.6825,5.7039 9.6825,5.8926 9.6392,6.0715C9.5999,6.2365 9.5115,6.3938 9.3327,6.7063L7.319,10.2658L7.3138,10.2749C7.1365,10.5853 7.0466,10.7426 6.922,10.8613C6.7864,10.991 6.6233,11.0853 6.4445,11.1384C6.2813,11.1836 6.0986,11.1836 5.733,11.1836ZM9.6538,11.1836H11.8785C12.2067,11.1836 12.3718,11.1836 12.4701,11.1188C12.5762,11.05 12.643,10.934 12.6489,10.8083C12.6546,10.6961 12.5758,10.5601 12.4213,10.2935C12.416,10.2845 12.4107,10.2752 12.4052,10.2658L11.2909,8.3595L11.2782,8.3381C11.1216,8.0733 11.0426,7.9395 10.941,7.8878C10.8291,7.8309 10.6973,7.8309 10.5853,7.8878C10.4831,7.9409 10.4006,8.0805 10.2375,8.3615L9.1271,10.2678L9.1233,10.2744C8.9607,10.555 8.8795,10.6952 8.8854,10.8102C8.8932,10.936 8.9581,11.0519 9.0642,11.1207C9.1605,11.1836 9.3256,11.1836 9.6538,11.1836Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_binance_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_binance_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,0L8,0A8,8 0,0 1,16 8L16,8A8,8 0,0 1,8 16L8,16A8,8 0,0 1,0 8L0,8A8,8 0,0 1,8 0z"/>
|
||||
<path android:fillColor="#A1A1A4" android:fillType="evenOdd" android:pathData="M6.0578,7.202L8.0002,5.2604L9.9434,7.2036L11.073,6.0733L8.0002,2.9998L4.9275,6.0725L6.0578,7.202ZM2.999,8.0005L4.129,6.8706L5.259,8.0005L4.129,9.1304L2.999,8.0005ZM8.0002,10.7408L6.0578,8.7985L4.9258,9.9272L4.9274,9.9288L8.0002,13.0007L11.073,9.9272L11.0738,9.9264L9.9434,8.7977L8.0002,10.7408ZM10.74,8.0009L11.87,6.871L12.9999,8.0009L11.87,9.1308L10.74,8.0009ZM8.0004,6.8526L9.1466,7.9995H9.1473L9.1466,8.0003L8.0004,9.1472L6.8542,8.0019L6.8526,7.9995L6.8542,7.9979L7.0549,7.7973L7.1528,7.7001L8.0004,6.8526Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_binance_round.xml
Normal file
6
app/src/main/res/drawable/ic_binance_round.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F3BA2F" android:pathData="M8,0L8,0A8,8 0,0 1,16 8L16,8A8,8 0,0 1,8 16L8,16A8,8 0,0 1,0 8L0,8A8,8 0,0 1,8 0z"/>
|
||||
<path android:fillColor="#ffffff" android:fillType="evenOdd" android:pathData="M6.0578,7.202L8.0002,5.2604L9.9434,7.2036L11.073,6.0733L8.0002,2.9998L4.9275,6.0725L6.0578,7.202ZM2.999,8.0005L4.129,6.8706L5.259,8.0005L4.129,9.1304L2.999,8.0005ZM8.0002,10.7408L6.0578,8.7985L4.9258,9.9272L4.9274,9.9288L8.0002,13.0007L11.073,9.9272L11.0738,9.9264L9.9434,8.7977L8.0002,10.7408ZM10.74,8.0009L11.87,6.871L12.9999,8.0009L11.87,9.1308L10.74,8.0009ZM8.0004,6.8526L9.1466,7.9995H9.1473L9.1466,8.0003L8.0004,9.1472L6.8542,8.0019L6.8526,7.9995L6.8542,7.9979L7.0549,7.7973L7.1528,7.7001L8.0004,6.8526Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_bitcoin_cash_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_bitcoin_cash_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,16C12.4183,16 16,12.4183 16,8C16,3.5817 12.4183,0 8,0C3.5817,0 0,3.5817 0,8C0,12.4183 3.5817,16 8,16Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M10.6797,5.3445C10.272,4.4259 9.3351,4.2295 8.1882,4.4198L7.8196,3L6.9507,3.223L7.3131,4.6387C7.0845,4.696 6.8498,4.7451 6.6171,4.8106L6.2547,3.403L5.3858,3.626L5.7543,5.0458C5.567,5.099 4,5.498 4,5.498L4.2389,6.4227C4.2389,6.4227 4.8772,6.2447 4.871,6.259C5.2252,6.1669 5.3919,6.3429 5.4702,6.5086L6.4833,10.3998C6.4956,10.5123 6.475,10.7046 6.232,10.77C6.2465,10.7782 5.5999,10.9317 5.5999,10.9317L5.6946,12.0098C5.6946,12.0098 7.2472,11.615 7.451,11.5638L7.8237,13L8.6927,12.777L8.32,11.3306C8.5588,11.2754 8.7915,11.2181 9.018,11.1588L9.3886,12.597L10.2576,12.374L9.8849,10.9399C11.2233,10.6166 12.1684,9.7778 11.9748,8.4951C11.8513,7.7218 11.0009,7.0876 10.2946,7.016C10.7291,6.6334 10.9494,6.0749 10.6797,5.3445ZM10.2617,8.7304C10.4346,10.0008 8.6577,10.1563 8.0708,10.3097L7.5602,8.4092C8.1491,8.2557 9.9693,7.6113 10.2617,8.7304ZM9.191,6.1424C9.3742,7.2717 7.8546,7.4026 7.3646,7.5274L6.8992,5.8028C7.3913,5.6821 8.8183,5.097 9.191,6.1424Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_bitcoin_cash_round.xml
Normal file
6
app/src/main/res/drawable/ic_bitcoin_cash_round.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#0AC18E" android:pathData="M8,16C12.4183,16 16,12.4183 16,8C16,3.5817 12.4183,0 8,0C3.5817,0 0,3.5817 0,8C0,12.4183 3.5817,16 8,16Z"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M10.6797,5.3445C10.272,4.4259 9.3351,4.2295 8.1882,4.4198L7.8196,3L6.9507,3.223L7.3131,4.6387C7.0845,4.696 6.8498,4.7451 6.6171,4.8106L6.2547,3.403L5.3858,3.626L5.7543,5.0458C5.567,5.099 4,5.498 4,5.498L4.2389,6.4227C4.2389,6.4227 4.8772,6.2447 4.871,6.259C5.2252,6.1669 5.3919,6.3429 5.4702,6.5086L6.4833,10.3998C6.4956,10.5123 6.475,10.7046 6.232,10.77C6.2465,10.7782 5.5999,10.9317 5.5999,10.9317L5.6946,12.0098C5.6946,12.0098 7.2472,11.615 7.451,11.5638L7.8237,13L8.6927,12.777L8.32,11.3306C8.5588,11.2754 8.7915,11.2181 9.018,11.1588L9.3886,12.597L10.2576,12.374L9.8849,10.9399C11.2233,10.6166 12.1684,9.7778 11.9748,8.4951C11.8513,7.7218 11.0009,7.0876 10.2946,7.016C10.7291,6.6334 10.9494,6.0749 10.6797,5.3445ZM10.2617,8.7304C10.4346,10.0008 8.6577,10.1563 8.0708,10.3097L7.5602,8.4092C8.1491,8.2557 9.9693,7.6113 10.2617,8.7304ZM9.191,6.1424C9.3742,7.2717 7.8546,7.4026 7.3646,7.5274L6.8992,5.8028C7.3913,5.6821 8.8183,5.097 9.191,6.1424Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_bitcoin_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_bitcoin_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M15.7605,9.9353C14.6919,14.221 10.3507,16.8292 6.064,15.7605C1.7791,14.692 -0.8295,10.351 0.2396,6.0656C1.3077,1.7794 5.649,-0.8291 9.9344,0.2394C14.2209,1.3079 16.8292,5.6493 15.7605,9.9353Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M11.5275,6.8603C11.6868,5.7958 10.8762,5.2236 9.7678,4.8418L10.1273,3.3998L9.2495,3.1811L8.8995,4.5851C8.6687,4.5276 8.4317,4.4733 8.1961,4.4196L8.5487,3.0064L7.6713,2.7876L7.3116,4.2291C7.1205,4.1856 6.933,4.1426 6.751,4.0973L6.752,4.0928L5.5414,3.7906L5.3078,4.7281C5.3078,4.7281 5.9592,4.8773 5.9454,4.8866C6.301,4.9753 6.3652,5.2106 6.3545,5.3971L5.9449,7.0398C5.9694,7.0461 6.0012,7.0551 6.0362,7.0691C6.0069,7.0618 5.9757,7.0538 5.9434,7.0461L5.3693,9.3473C5.3258,9.4553 5.2156,9.6173 4.9671,9.5558C4.9758,9.5685 4.329,9.3965 4.329,9.3965L3.8932,10.4013L5.0356,10.686C5.2481,10.7393 5.4563,10.795 5.6614,10.8475L5.2981,12.306L6.1749,12.5248L6.5347,11.0818C6.7742,11.1468 7.0068,11.2068 7.2343,11.2633L6.8758,12.6995L7.7536,12.9182L8.1169,11.4625C9.6138,11.7458 10.7394,11.6315 11.2132,10.2778C11.595,9.1878 11.1942,8.559 10.4066,8.149C10.9802,8.0168 11.4122,7.6396 11.5275,6.8603ZM9.5218,9.6725C9.2505,10.7625 7.4151,10.1733 6.82,10.0255L7.3021,8.0933C7.8971,8.2418 9.8053,8.5358 9.5218,9.6725ZM9.7933,6.8446C9.5458,7.8361 8.0181,7.3323 7.5226,7.2088L7.9596,5.4563C8.4552,5.5798 10.0511,5.8103 9.7933,6.8446Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_bitcoin_round.xml
Normal file
6
app/src/main/res/drawable/ic_bitcoin_round.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F7931A" android:pathData="M15.7605,9.9353C14.6919,14.221 10.3507,16.8292 6.064,15.7605C1.7791,14.692 -0.8295,10.351 0.2396,6.0656C1.3077,1.7794 5.649,-0.8291 9.9344,0.2394C14.2209,1.3079 16.8292,5.6493 15.7605,9.9353Z"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M11.5275,6.8603C11.6868,5.7958 10.8762,5.2236 9.7678,4.8418L10.1273,3.3998L9.2495,3.1811L8.8995,4.5851C8.6687,4.5276 8.4317,4.4733 8.1961,4.4196L8.5487,3.0064L7.6713,2.7876L7.3116,4.2291C7.1205,4.1856 6.933,4.1426 6.751,4.0973L6.752,4.0928L5.5414,3.7906L5.3078,4.7281C5.3078,4.7281 5.9592,4.8773 5.9454,4.8866C6.301,4.9753 6.3652,5.2106 6.3545,5.3971L5.9449,7.0398C5.9694,7.0461 6.0012,7.0551 6.0362,7.0691C6.0069,7.0618 5.9757,7.0538 5.9434,7.0461L5.3693,9.3473C5.3258,9.4553 5.2156,9.6173 4.9671,9.5558C4.9758,9.5685 4.329,9.3965 4.329,9.3965L3.8932,10.4013L5.0356,10.686C5.2481,10.7393 5.4563,10.795 5.6614,10.8475L5.2981,12.306L6.1749,12.5248L6.5347,11.0818C6.7742,11.1468 7.0068,11.2068 7.2343,11.2633L6.8758,12.6995L7.7536,12.9182L8.1169,11.4625C9.6138,11.7458 10.7394,11.6315 11.2132,10.2778C11.595,9.1878 11.1942,8.559 10.4066,8.149C10.9802,8.0168 11.4122,7.6396 11.5275,6.8603ZM9.5218,9.6725C9.2505,10.7625 7.4151,10.1733 6.82,10.0255L7.3021,8.0933C7.8971,8.2418 9.8053,8.5358 9.5218,9.6725ZM9.7933,6.8446C9.5458,7.8361 8.0181,7.3323 7.5226,7.2088L7.9596,5.4563C8.4552,5.5798 10.0511,5.8103 9.7933,6.8446Z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_bsc_no_color.xml
Normal file
9
app/src/main/res/drawable/ic_bsc_no_color.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:fillType="evenOdd" android:pathData="M8,0C12.4186,0 16,3.5814 16,8C16,12.4186 12.4186,16 8,16C3.5814,16 0,12.4186 0,8C0,3.5814 3.5814,0 8,0Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M4.3968,8.0001L4.4025,10.1154L6.2,11.1731V12.4116L3.3506,10.7404V7.3815L4.3968,8.0001ZM4.3968,5.8847V7.1174L3.35,6.4981V5.2655L4.3968,4.6462L5.4487,5.2655L4.3968,5.8847ZM6.9506,5.2655L7.9974,4.6462L9.0493,5.2655L7.9974,5.8847L6.9506,5.2655Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M5.1532,9.677V8.4385L6.2,9.0577V10.2904L5.1532,9.677ZM6.9506,11.6167L7.9974,12.2359L9.0493,11.6167V12.8494L7.9974,13.4686L6.9506,12.8494V11.6167ZM10.5505,5.2655L11.5973,4.6462L12.6492,5.2655V6.4981L11.5973,7.1174V5.8847L10.5505,5.2655ZM11.5973,10.1154L11.6031,8.0001L12.6499,7.3808V10.7398L9.8006,12.4109V11.1725L11.5973,10.1154Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M10.8468,9.6769L9.8,10.2903V9.0577L10.8468,8.4385V9.6769Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M10.8468,6.3232L10.8526,7.5616L9.05,8.6193V10.7398L8.0032,11.3533L6.9564,10.7398V8.6193L5.1538,7.5616V6.3232L6.2051,5.7039L7.9968,6.7667L9.7993,5.7039L10.8513,6.3232H10.8468ZM5.1532,4.2084L7.9974,2.5315L10.8468,4.2084L9.8,4.8276L7.9974,3.7648L6.2,4.8276L5.1532,4.2084Z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_bsc_round.xml
Normal file
9
app/src/main/res/drawable/ic_bsc_round.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#000000" android:fillType="evenOdd" android:pathData="M8,0C12.4186,0 16,3.5814 16,8C16,12.4186 12.4186,16 8,16C3.5814,16 0,12.4186 0,8C0,3.5814 3.5814,0 8,0Z"/>
|
||||
<path android:fillColor="#F0B90B" android:pathData="M4.3968,8.0001L4.4025,10.1154L6.2,11.1731V12.4116L3.3506,10.7404V7.3815L4.3968,8.0001ZM4.3968,5.8847V7.1174L3.35,6.4981V5.2655L4.3968,4.6462L5.4487,5.2655L4.3968,5.8847ZM6.9506,5.2655L7.9974,4.6462L9.0493,5.2655L7.9974,5.8847L6.9506,5.2655Z"/>
|
||||
<path android:fillColor="#F0B90B" android:pathData="M5.1532,9.677V8.4385L6.2,9.0577V10.2904L5.1532,9.677ZM6.9506,11.6167L7.9974,12.2359L9.0493,11.6167V12.8494L7.9974,13.4686L6.9506,12.8494V11.6167ZM10.5505,5.2655L11.5973,4.6462L12.6492,5.2655V6.4981L11.5973,7.1174V5.8847L10.5505,5.2655ZM11.5973,10.1154L11.6031,8.0001L12.6499,7.3808V10.7398L9.8006,12.4109V11.1725L11.5973,10.1154Z"/>
|
||||
<path android:fillColor="#F0B90B" android:pathData="M10.8468,9.6769L9.8,10.2903V9.0577L10.8468,8.4385V9.6769Z"/>
|
||||
<path android:fillColor="#F0B90B" android:pathData="M10.8468,6.3232L10.8526,7.5616L9.05,8.6193V10.7398L8.0032,11.3533L6.9564,10.7398V8.6193L5.1538,7.5616V6.3232L6.2051,5.7039L7.9968,6.7667L9.7993,5.7039L10.8513,6.3232H10.8468ZM5.1532,4.2084L7.9974,2.5315L10.8468,4.2084L9.8,4.8276L7.9974,3.7648L6.2,4.8276L5.1532,4.2084Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_cardano_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_cardano_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M7.8738,3.2662C8.098,3.1493 8.3717,3.4204 8.2526,3.6444C8.1834,3.827 7.9211,3.8755 7.7871,3.7364C7.6452,3.6028 7.6915,3.3388 7.8738,3.2662ZM5.4626,3.5261C5.5988,3.47 5.7712,3.5948 5.7573,3.7432C5.7718,3.9043 5.5747,4.0229 5.4359,3.9464C5.252,3.8751 5.273,3.5766 5.4626,3.5261ZM10.4011,3.9509C10.1883,3.9252 10.1546,3.5906 10.3595,3.5232C10.5184,3.4457 10.6642,3.591 10.6875,3.7417C10.6541,3.8703 10.5465,3.9881 10.4011,3.9509ZM6.0113,4.5523C6.2389,4.4083 6.5638,4.6228 6.5288,4.8872C6.517,5.1543 6.1623,5.309 5.9622,5.1284C5.7687,4.9867 5.798,4.6614 6.0113,4.5523ZM9.4869,4.7051C9.5884,4.4434 10,4.4354 10.1156,4.6903C10.2299,4.8886 10.0894,5.1342 9.8857,5.2016C9.616,5.2519 9.3599,4.9604 9.4869,4.7051ZM7.5835,5.1867C7.5813,4.9617 7.7862,4.7941 8.0001,4.7787C8.1419,4.8072 8.2965,4.8676 8.3573,5.0107C8.4707,5.2082 8.37,5.48 8.1651,5.5706C8.0714,5.6231 7.9607,5.6034 7.8586,5.5973C7.7027,5.5208 7.5713,5.3706 7.5835,5.1867ZM3.9976,5.5146C4.2056,5.3867 4.4862,5.62 4.4075,5.846C4.3641,6.0414 4.1014,6.1209 3.9512,5.9962C3.7909,5.8793 3.8163,5.5982 3.9976,5.5146ZM11.6978,5.5142C11.8742,5.3772 12.1654,5.5343 12.1444,5.7571C12.1562,5.9603 11.9045,6.1135 11.7293,6.0089C11.5364,5.9222 11.5167,5.6253 11.6978,5.5142ZM8.5919,5.9187C8.8831,5.814 9.2289,5.9392 9.3949,6.1988C9.6138,6.5162 9.4974,6.9995 9.1576,7.1816C8.8056,7.397 8.2916,7.2144 8.1615,6.8213C8.014,6.4694 8.229,6.029 8.5919,5.9187ZM6.8834,5.9533C7.2062,5.8009 7.6405,5.9287 7.7964,6.2589C7.9798,6.5802 7.838,7.0276 7.51,7.1935C7.1816,7.38 6.7183,7.2386 6.5594,6.8936C6.3812,6.56 6.5395,6.1069 6.8834,5.9533ZM5.1728,6.5381C5.1973,6.3302 5.3934,6.1949 5.5913,6.1809C5.8046,6.2066 5.9749,6.3722 5.9981,6.588C5.9841,6.8016 5.819,7.0104 5.5926,7.0091C5.3436,7.0306 5.1252,6.7828 5.1728,6.5381ZM10.2255,6.2261C10.4812,6.0773 10.8363,6.2939 10.8249,6.5877C10.8425,6.8989 10.4495,7.1183 10.1949,6.9357C9.9304,6.7837 9.951,6.3555 10.2255,6.2261ZM6.1882,7.3231C6.4872,7.2408 6.8292,7.3818 6.9759,7.6566C7.1365,7.9333 7.0708,8.3177 6.8213,8.5206C6.5013,8.8183 5.911,8.6988 5.7407,8.2926C5.545,7.9201 5.7827,7.4211 6.1882,7.3231ZM9.4286,7.3215C9.7194,7.2384 10.0609,7.3504 10.2172,7.6153C10.4238,7.9182 10.3252,8.37 10.0176,8.5653C9.6953,8.792 9.1961,8.673 9.0188,8.3184C8.8038,7.9517 9.0202,7.4334 9.4286,7.3215ZM4.2647,7.6671C4.523,7.5694 4.8094,7.8545 4.7047,8.1106C4.6408,8.3439 4.312,8.4302 4.1421,8.2581C3.9433,8.0966 4.0169,7.7372 4.2647,7.6671ZM11.2667,8.0388C11.2593,7.8247 11.4159,7.6352 11.6354,7.6234C11.8123,7.6514 11.9843,7.7941 11.9703,7.9867C11.9865,8.2411 11.68,8.414 11.4633,8.2944C11.3617,8.2428 11.3096,8.1386 11.2667,8.0388ZM2.9945,7.7928C3.1499,7.7249 3.3422,7.8596 3.3067,8.0322C3.2983,8.2276 3.0098,8.3028 2.9038,8.1395C2.8102,8.0261 2.8662,7.8512 2.9945,7.7928ZM12.7939,7.7889C12.9033,7.7109 13.0789,7.767 13.1223,7.8948C13.1958,8.0344 13.061,8.2257 12.9038,8.2021C12.6832,8.2222 12.6118,7.886 12.7939,7.7889ZM7.0323,8.727C7.4413,8.6206 7.8809,8.9616 7.8835,9.3827C7.9102,9.8083 7.4772,10.1856 7.0585,10.0873C6.735,10.0352 6.4811,9.7235 6.4915,9.3965C6.4907,9.0838 6.7267,8.7931 7.0323,8.727ZM8.6519,8.7261C9.067,8.6101 9.5197,8.9572 9.5131,9.388C9.5351,9.8057 9.1156,10.1703 8.7057,10.0875C8.32,10.0376 8.0354,9.6138 8.1431,9.2395C8.1974,8.9901 8.4058,8.7872 8.6519,8.7261ZM5.5292,8.9853C5.8165,8.9363 6.0857,9.2567 5.9692,9.5295C5.8843,9.8201 5.4613,9.8989 5.2743,9.6615C5.055,9.4335 5.2187,9.0168 5.5292,8.9853ZM10.2789,8.9981C10.535,8.8908 10.8494,9.1124 10.8297,9.3904C10.84,9.6964 10.4519,9.9074 10.2014,9.7288C9.9177,9.5684 9.9694,9.0965 10.2789,8.9981ZM11.6065,10.3297C11.5062,10.1427 11.6884,9.895 11.8959,9.9296C11.9993,9.9278 12.0715,10.0092 12.1377,10.0775C12.1512,10.1883 12.174,10.3205 12.0821,10.4059C11.9572,10.5639 11.6743,10.5188 11.6065,10.3297ZM3.9968,9.9816C4.1907,9.8525 4.4699,10.0451 4.4263,10.2719C4.4031,10.4725 4.1504,10.5871 3.984,10.4733C3.8054,10.37 3.812,10.0758 3.9968,9.9816ZM7.8292,10.4015C8.0832,10.2894 8.4067,10.5105 8.3826,10.7902C8.4015,11.0993 8.0013,11.3134 7.753,11.1278C7.4772,10.9684 7.5232,10.4995 7.8292,10.4015ZM6.0542,10.8156C6.2705,10.7018 6.5607,10.8983 6.541,11.1404C6.5476,11.3392 6.3536,11.5086 6.1575,11.4792C5.995,11.4757 5.8891,11.3301 5.837,11.1916C5.8396,11.0406 5.9018,10.8725 6.0542,10.8156ZM9.6497,10.8191C9.8722,10.6816 10.1883,10.8791 10.1673,11.1379C10.173,11.4085 9.8157,11.5818 9.6086,11.4041C9.4081,11.2648 9.4326,10.9299 9.6497,10.8191ZM10.275,12.3649C10.1761,12.2152 10.2925,12.0348 10.4532,12.0007C10.5824,12.0269 10.719,12.1241 10.6918,12.2739C10.673,12.4765 10.3766,12.5387 10.275,12.3649ZM5.3116,12.2349C5.351,12.1162 5.4464,11.9983 5.5861,12.0292C5.7897,12.0436 5.8501,12.3505 5.6719,12.4451C5.5117,12.5531 5.3258,12.4051 5.3116,12.2349ZM7.7062,12.371C7.7451,12.1955 7.9723,12.1101 8.1221,12.2033C8.2211,12.2458 8.25,12.3565 8.2754,12.4507C8.2618,12.4997 8.2496,12.5487 8.2377,12.5978C8.1764,12.6704 8.0976,12.7431 7.9956,12.7405C7.806,12.7627 7.6349,12.5483 7.7062,12.371Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_cardano_round.xml
Normal file
6
app/src/main/res/drawable/ic_cardano_round.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#0033AD" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M7.8738,3.2662C8.098,3.1493 8.3717,3.4204 8.2526,3.6444C8.1834,3.827 7.9211,3.8755 7.7871,3.7364C7.6452,3.6028 7.6915,3.3388 7.8738,3.2662ZM5.4626,3.5261C5.5988,3.47 5.7712,3.5948 5.7573,3.7432C5.7718,3.9043 5.5747,4.0229 5.4359,3.9464C5.252,3.8751 5.273,3.5766 5.4626,3.5261ZM10.4011,3.9509C10.1883,3.9252 10.1546,3.5906 10.3595,3.5232C10.5184,3.4457 10.6642,3.591 10.6875,3.7417C10.6541,3.8703 10.5465,3.9881 10.4011,3.9509ZM6.0113,4.5523C6.2389,4.4083 6.5638,4.6228 6.5288,4.8872C6.517,5.1543 6.1623,5.309 5.9622,5.1284C5.7687,4.9867 5.798,4.6614 6.0113,4.5523ZM9.4869,4.7051C9.5884,4.4434 10,4.4354 10.1156,4.6903C10.2299,4.8886 10.0894,5.1342 9.8857,5.2016C9.616,5.2519 9.3599,4.9604 9.4869,4.7051ZM7.5835,5.1867C7.5813,4.9617 7.7862,4.7941 8.0001,4.7787C8.1419,4.8072 8.2965,4.8676 8.3573,5.0107C8.4707,5.2082 8.37,5.48 8.1651,5.5706C8.0714,5.6231 7.9607,5.6034 7.8586,5.5973C7.7027,5.5208 7.5713,5.3706 7.5835,5.1867ZM3.9976,5.5146C4.2056,5.3867 4.4862,5.62 4.4075,5.846C4.3641,6.0414 4.1014,6.1209 3.9512,5.9962C3.7909,5.8793 3.8163,5.5982 3.9976,5.5146ZM11.6978,5.5142C11.8742,5.3772 12.1654,5.5343 12.1444,5.7571C12.1562,5.9603 11.9045,6.1135 11.7293,6.0089C11.5364,5.9222 11.5167,5.6253 11.6978,5.5142ZM8.5919,5.9187C8.8831,5.814 9.2289,5.9392 9.3949,6.1988C9.6138,6.5162 9.4974,6.9995 9.1576,7.1816C8.8056,7.397 8.2916,7.2144 8.1615,6.8213C8.014,6.4694 8.229,6.029 8.5919,5.9187ZM6.8834,5.9533C7.2062,5.8009 7.6405,5.9287 7.7964,6.2589C7.9798,6.5802 7.838,7.0276 7.51,7.1935C7.1816,7.38 6.7183,7.2386 6.5594,6.8936C6.3812,6.56 6.5395,6.1069 6.8834,5.9533ZM5.1728,6.5381C5.1973,6.3302 5.3934,6.1949 5.5913,6.1809C5.8046,6.2066 5.9749,6.3722 5.9981,6.588C5.9841,6.8016 5.819,7.0104 5.5926,7.0091C5.3436,7.0306 5.1252,6.7828 5.1728,6.5381ZM10.2255,6.2261C10.4812,6.0773 10.8363,6.2939 10.8249,6.5877C10.8425,6.8989 10.4495,7.1183 10.1949,6.9357C9.9304,6.7837 9.951,6.3555 10.2255,6.2261ZM6.1882,7.3231C6.4872,7.2408 6.8292,7.3818 6.9759,7.6566C7.1365,7.9333 7.0708,8.3177 6.8213,8.5206C6.5013,8.8183 5.911,8.6988 5.7407,8.2926C5.545,7.9201 5.7827,7.4211 6.1882,7.3231ZM9.4286,7.3215C9.7194,7.2384 10.0609,7.3504 10.2172,7.6153C10.4238,7.9182 10.3252,8.37 10.0176,8.5653C9.6953,8.792 9.1961,8.673 9.0188,8.3184C8.8038,7.9517 9.0202,7.4334 9.4286,7.3215ZM4.2647,7.6671C4.523,7.5694 4.8094,7.8545 4.7047,8.1106C4.6408,8.3439 4.312,8.4302 4.1421,8.2581C3.9433,8.0966 4.0169,7.7372 4.2647,7.6671ZM11.2667,8.0388C11.2593,7.8247 11.4159,7.6352 11.6354,7.6234C11.8123,7.6514 11.9843,7.7941 11.9703,7.9867C11.9865,8.2411 11.68,8.414 11.4633,8.2944C11.3617,8.2428 11.3096,8.1386 11.2667,8.0388ZM2.9945,7.7928C3.1499,7.7249 3.3422,7.8596 3.3067,8.0322C3.2983,8.2276 3.0098,8.3028 2.9038,8.1395C2.8102,8.0261 2.8662,7.8512 2.9945,7.7928ZM12.7939,7.7889C12.9033,7.7109 13.0789,7.767 13.1223,7.8948C13.1958,8.0344 13.061,8.2257 12.9038,8.2021C12.6832,8.2222 12.6118,7.886 12.7939,7.7889ZM7.0323,8.727C7.4413,8.6206 7.8809,8.9616 7.8835,9.3827C7.9102,9.8083 7.4772,10.1856 7.0585,10.0873C6.735,10.0352 6.4811,9.7235 6.4915,9.3965C6.4907,9.0838 6.7267,8.7931 7.0323,8.727ZM8.6519,8.7261C9.067,8.6101 9.5197,8.9572 9.5131,9.388C9.5351,9.8057 9.1156,10.1703 8.7057,10.0875C8.32,10.0376 8.0354,9.6138 8.1431,9.2395C8.1974,8.9901 8.4058,8.7872 8.6519,8.7261ZM5.5292,8.9853C5.8165,8.9363 6.0857,9.2567 5.9692,9.5295C5.8843,9.8201 5.4613,9.8989 5.2743,9.6615C5.055,9.4335 5.2187,9.0168 5.5292,8.9853ZM10.2789,8.9981C10.535,8.8908 10.8494,9.1124 10.8297,9.3904C10.84,9.6964 10.4519,9.9074 10.2014,9.7288C9.9177,9.5684 9.9694,9.0965 10.2789,8.9981ZM11.6065,10.3297C11.5062,10.1427 11.6884,9.895 11.8959,9.9296C11.9993,9.9278 12.0715,10.0092 12.1377,10.0775C12.1512,10.1883 12.174,10.3205 12.0821,10.4059C11.9572,10.5639 11.6743,10.5188 11.6065,10.3297ZM3.9968,9.9816C4.1907,9.8525 4.4699,10.0451 4.4263,10.2719C4.4031,10.4725 4.1504,10.5871 3.984,10.4733C3.8054,10.37 3.812,10.0758 3.9968,9.9816ZM7.8292,10.4015C8.0832,10.2894 8.4067,10.5105 8.3826,10.7902C8.4015,11.0993 8.0013,11.3134 7.753,11.1278C7.4772,10.9684 7.5232,10.4995 7.8292,10.4015ZM6.0542,10.8156C6.2705,10.7018 6.5607,10.8983 6.541,11.1404C6.5476,11.3392 6.3536,11.5086 6.1575,11.4792C5.995,11.4757 5.8891,11.3301 5.837,11.1916C5.8396,11.0406 5.9018,10.8725 6.0542,10.8156ZM9.6497,10.8191C9.8722,10.6816 10.1883,10.8791 10.1673,11.1379C10.173,11.4085 9.8157,11.5818 9.6086,11.4041C9.4081,11.2648 9.4326,10.9299 9.6497,10.8191ZM10.275,12.3649C10.1761,12.2152 10.2925,12.0348 10.4532,12.0007C10.5824,12.0269 10.719,12.1241 10.6918,12.2739C10.673,12.4765 10.3766,12.5387 10.275,12.3649ZM5.3116,12.2349C5.351,12.1162 5.4464,11.9983 5.5861,12.0292C5.7897,12.0436 5.8501,12.3505 5.6719,12.4451C5.5117,12.5531 5.3258,12.4051 5.3116,12.2349ZM7.7062,12.371C7.7451,12.1955 7.9723,12.1101 8.1221,12.2033C8.2211,12.2458 8.25,12.3565 8.2754,12.4507C8.2618,12.4997 8.2496,12.5487 8.2377,12.5978C8.1764,12.6704 8.0976,12.7431 7.9956,12.7405C7.806,12.7627 7.6349,12.5483 7.7062,12.371Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_dogecoin_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_dogecoin_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#A1A1A4" android:fillType="evenOdd" android:pathData="M5.3545,12.185H8.1164H8.1165C8.1165,12.185 11.9999,12.5157 11.9999,8.0674C11.9999,3.7917 8.4536,3.8068 7.7682,3.8098C7.7521,3.8098 7.7376,3.8099 7.7248,3.8099H5.3544V7.5406H4.3809V8.4545H5.3545V12.185ZM6.9101,5.3579H7.9998C8.4074,5.3579 10.4578,5.5245 10.4611,8.0975C10.4643,10.6399 8.397,10.6373 8.0645,10.6368C8.0604,10.6368 8.0567,10.6368 8.0532,10.6368H6.9101V8.4544H8.6245V7.5405H6.9101V5.3579Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_dogecoin_round.xml
Normal file
6
app/src/main/res/drawable/ic_dogecoin_round.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#C2A633" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#ffffff" android:fillType="evenOdd" android:pathData="M5.3545,12.185H8.1164H8.1165C8.1165,12.185 11.9999,12.5157 11.9999,8.0674C11.9999,3.7917 8.4536,3.8068 7.7682,3.8098C7.7521,3.8098 7.7376,3.8099 7.7248,3.8099H5.3544V7.5406H4.3809V8.4545H5.3545V12.185ZM6.9101,5.3579H7.9998C8.4074,5.3579 10.4578,5.5245 10.4611,8.0975C10.4643,10.6399 8.397,10.6373 8.0645,10.6368C8.0604,10.6368 8.0567,10.6368 8.0532,10.6368H6.9101V8.4544H8.6245V7.5405H6.9101V5.3579Z"/>
|
||||
</vector>
|
||||
14
app/src/main/res/drawable/ic_eth_no_color.xml
Normal file
14
app/src/main/res/drawable/ic_eth_no_color.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,16C12.4183,16 16,12.4183 16,8C16,3.5817 12.4183,0 8,0C3.5817,0 0,3.5817 0,8C0,12.4183 3.5817,16 8,16Z"/>
|
||||
<group>
|
||||
<clip-path android:pathData="M8.0002,2.8001L8.0002,2.8001A3.2001,3.2001 0,0 1,11.2003 6.0001L11.2003,10.0003A3.2001,3.2001 0,0 1,8.0002 13.2003L8.0002,13.2003A3.2001,3.2001 0,0 1,4.8002 10.0003L4.8002,6.0001A3.2001,3.2001 0,0 1,8.0002 2.8001z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M7.9994,2.8001L7.9296,3.037V9.9123L7.9994,9.9819L11.1986,8.0955L7.9994,2.8001Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M7.9993,2.8001L4.8002,8.0955L7.9993,9.9819V6.6449V2.8001Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M7.9992,10.5862L7.9598,10.6341V13.0832L7.9992,13.198L11.2003,8.7007L7.9992,10.5862Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M7.9993,13.1982V10.5864L4.8002,8.7009L7.9993,13.1982Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M7.9991,9.9821L11.1982,8.0956L7.9991,6.645V9.9821Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M4.8002,8.0956L7.9992,9.9821V6.645L4.8002,8.0956Z"/>
|
||||
</group>
|
||||
</vector>
|
||||
20
app/src/main/res/drawable/ic_eth_round.xml
Normal file
20
app/src/main/res/drawable/ic_eth_round.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,16C12.4183,16 16,12.4183 16,8C16,3.5817 12.4183,0 8,0C3.5817,0 0,3.5817 0,8C0,12.4183 3.5817,16 8,16Z"/>
|
||||
<group>
|
||||
<clip-path android:pathData="M8,2.8001L8,2.8001A3.2,3.2 0,0 1,11.2001 6.0001L11.2001,10A3.2,3.2 0,0 1,8 13.2001L8,13.2001A3.2,3.2 0,0 1,4.8 10L4.8,6.0001A3.2,3.2 0,0 1,8 2.8001z"/>
|
||||
<path android:fillAlpha="0.8" android:fillColor="#343434"
|
||||
android:pathData="M7.9992,2.8001L7.9293,3.037V9.9122L7.9992,9.9817L11.1982,8.0953L7.9992,2.8001Z" android:strokeAlpha="0.8"/>
|
||||
<path android:fillAlpha="0.8" android:fillColor="#8C8C8C"
|
||||
android:pathData="M7.9991,2.8001L4.8,8.0953L7.9991,9.9818V6.6448V2.8001Z" android:strokeAlpha="0.8"/>
|
||||
<path android:fillAlpha="0.8" android:fillColor="#3C3C3B"
|
||||
android:pathData="M7.999,10.5862L7.9596,10.634V13.0831L7.999,13.1978L11.1999,8.7007L7.999,10.5862Z" android:strokeAlpha="0.8"/>
|
||||
<path android:fillAlpha="0.8" android:fillColor="#8C8C8C"
|
||||
android:pathData="M7.9991,13.1979V10.5862L4.8,8.7007L7.9991,13.1979Z" android:strokeAlpha="0.8"/>
|
||||
<path android:fillAlpha="0.8" android:fillColor="#141414"
|
||||
android:pathData="M7.999,9.9817L11.198,8.0953L7.999,6.6448V9.9817Z" android:strokeAlpha="0.8"/>
|
||||
<path android:fillAlpha="0.8" android:fillColor="#393939"
|
||||
android:pathData="M4.8,8.0953L7.9991,9.9818V6.6448L4.8,8.0953Z" android:strokeAlpha="0.8"/>
|
||||
</group>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_fantom_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_fantom_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="22dp"
|
||||
android:viewportHeight="22" android:viewportWidth="22"
|
||||
android:width="22dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M11,22C17.0751,22 22,17.0751 22,11C22,4.9249 17.0751,0 11,0C4.9249,0 0,4.9249 0,11C0,17.0751 4.9249,22 11,22Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:fillType="evenOdd" android:pathData="M11.825,8.8687L14.3,7.425V10.3125L11.825,8.8687ZM14.3,15.0562L11,16.9813L7.7,15.0562V11.6875L11,13.6125L14.3,11.6875V15.0562ZM7.7,7.425L10.175,8.8687L7.7,10.3125V7.425ZM11.4125,9.5562L13.8875,11L11.4125,12.4438V9.5562ZM10.5875,12.4438L8.1125,11L10.5875,9.5562V12.4438ZM13.8875,6.7375L11,8.3875L8.1125,6.7375L11,5.0188L13.8875,6.7375ZM6.875,6.4625V15.4688L11,17.8062L15.125,15.4688V6.4625L11,4.125L6.875,6.4625Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_fantom_round.xml
Normal file
6
app/src/main/res/drawable/ic_fantom_round.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="22dp"
|
||||
android:viewportHeight="22" android:viewportWidth="22"
|
||||
android:width="22dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#13B5EC" android:pathData="M11,22C17.0751,22 22,17.0751 22,11C22,4.9249 17.0751,0 11,0C4.9249,0 0,4.9249 0,11C0,17.0751 4.9249,22 11,22Z"/>
|
||||
<path android:fillColor="#F8F9F9" android:fillType="evenOdd" android:pathData="M11.825,8.8687L14.3,7.425V10.3125L11.825,8.8687ZM14.3,15.0562L11,16.9813L7.7,15.0562V11.6875L11,13.6125L14.3,11.6875V15.0562ZM7.7,7.425L10.175,8.8687L7.7,10.3125V7.425ZM11.4125,9.5562L13.8875,11L11.4125,12.4438V9.5562ZM10.5875,12.4438L8.1125,11L10.5875,9.5562V12.4438ZM13.8875,6.7375L11,8.3875L8.1125,6.7375L11,5.0188L13.8875,6.7375ZM6.875,6.4625V15.4688L11,17.8062L15.125,15.4688V6.4625L11,4.125L6.875,6.4625Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_kusama_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_kusama_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M12.4155,5.3206C12.265,5.2041 12.0856,5.0449 11.7587,5.0051C11.452,4.9653 11.1395,5.1671 10.9282,5.3007C10.717,5.4343 10.3177,5.8266 10.1528,5.946C9.9879,6.0654 9.5654,6.1762 8.8854,6.577C8.2054,6.9778 5.5376,8.6606 5.5376,8.6606L6.2321,8.6691L3.136,10.2354H3.4456L3,10.5679C3,10.5679 3.3935,10.6703 3.7234,10.4656V10.5594C3.7234,10.5594 7.4097,9.1325 8.1215,9.502L7.6875,9.6271C7.7251,9.6271 8.4254,9.6725 8.4254,9.6725C8.4423,9.815 8.4911,9.952 8.5681,10.0738C8.6451,10.1957 8.7485,10.2994 8.8709,10.3775C9.2934,10.6504 9.3021,10.801 9.3021,10.801C9.3021,10.801 9.0822,10.8891 9.0822,11C9.0822,11 9.4063,10.9034 9.7072,10.9119C9.8982,10.919 10.0876,10.9486 10.2714,11C10.2714,11 10.2483,10.8806 9.956,10.801C9.6638,10.7214 9.3744,10.4088 9.2326,10.2382C9.1458,10.1296 9.0892,10.0008 9.0684,9.8642C9.0476,9.7276 9.0633,9.5881 9.114,9.4593C9.2153,9.2007 9.5683,9.0585 10.2975,8.689C11.1568,8.2513 11.3536,7.9272 11.4751,7.6742C11.5966,7.4212 11.776,6.9181 11.8773,6.6822C12.0046,6.378 12.1609,6.216 12.2911,6.1194C12.4213,6.0227 13,5.8095 13,5.8095C13,5.8095 12.5573,5.4315 12.4155,5.3206Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_kusama_round.xml
Normal file
6
app/src/main/res/drawable/ic_kusama_round.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#000000" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M12.4155,5.3206C12.265,5.2041 12.0856,5.0449 11.7587,5.0051C11.452,4.9653 11.1395,5.1671 10.9282,5.3007C10.717,5.4343 10.3177,5.8266 10.1528,5.946C9.9879,6.0654 9.5654,6.1762 8.8854,6.577C8.2054,6.9778 5.5376,8.6606 5.5376,8.6606L6.2321,8.6691L3.136,10.2354H3.4456L3,10.5679C3,10.5679 3.3935,10.6703 3.7234,10.4656V10.5594C3.7234,10.5594 7.4097,9.1325 8.1215,9.502L7.6875,9.6271C7.7251,9.6271 8.4254,9.6725 8.4254,9.6725C8.4423,9.815 8.4911,9.952 8.5681,10.0738C8.6451,10.1957 8.7485,10.2994 8.8709,10.3775C9.2934,10.6504 9.3021,10.801 9.3021,10.801C9.3021,10.801 9.0822,10.8891 9.0822,11C9.0822,11 9.4063,10.9034 9.7072,10.9119C9.8982,10.919 10.0876,10.9486 10.2714,11C10.2714,11 10.2483,10.8806 9.956,10.801C9.6638,10.7214 9.3744,10.4088 9.2326,10.2382C9.1458,10.1296 9.0892,10.0008 9.0684,9.8642C9.0476,9.7276 9.0633,9.5881 9.114,9.4593C9.2153,9.2007 9.5683,9.0585 10.2975,8.689C11.1568,8.2513 11.3536,7.9272 11.4751,7.6742C11.5966,7.4212 11.776,6.9181 11.8773,6.6822C12.0046,6.378 12.1609,6.216 12.2911,6.1194C12.4213,6.0227 13,5.8095 13,5.8095C13,5.8095 12.5573,5.4315 12.4155,5.3206Z"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_link.xml
Normal file
10
app/src/main/res/drawable/ic_link.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector android:autoMirrored="true" android:height="38dp"
|
||||
android:viewportHeight="38" android:viewportWidth="32"
|
||||
android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M1,0L1,14C1,25.0457 9.9543,34 21,34H30"
|
||||
android:strokeColor="#DEDEDE" android:strokeWidth="1"/>
|
||||
<path android:fillColor="#DEDEDE"
|
||||
android:pathData="M26.75,31.8822V36.1185L30.8871,34.0004L26.75,31.8822Z"
|
||||
android:strokeColor="#DEDEDE" android:strokeWidth="1.5"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_link_long.xml
Normal file
10
app/src/main/res/drawable/ic_link_long.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector android:autoMirrored="true" android:height="89dp"
|
||||
android:viewportHeight="89" android:viewportWidth="32"
|
||||
android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M1,0L1,65C1,76.0457 9.9543,85 21,85H30"
|
||||
android:strokeColor="#DEDEDE" android:strokeWidth="1"/>
|
||||
<path android:fillColor="#DEDEDE"
|
||||
android:pathData="M26.75,82.8822V87.1185L30.8871,85.0004L26.75,82.8822Z"
|
||||
android:strokeColor="#DEDEDE" android:strokeWidth="1.5"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_litecoin_no_color.xml
Normal file
9
app/src/main/res/drawable/ic_litecoin_no_color.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group>
|
||||
<clip-path android:pathData="M0,0h16v16h-16z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M8,16C12.4183,16 16,12.4183 16,8C16,3.5817 12.4183,0 8,0C3.5817,0 0,3.5817 0,8C0,12.4183 3.5817,16 8,16Z"/>
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,0C6.4177,0 4.871,0.4692 3.5554,1.3482C2.2399,2.2273 1.2145,3.4767 0.609,4.9385C0.0035,6.4003 -0.155,8.0089 0.1537,9.5607C0.4624,11.1126 1.2243,12.538 2.3431,13.6569C3.462,14.7757 4.8874,15.5376 6.4393,15.8463C7.9911,16.155 9.5997,15.9965 11.0615,15.391C12.5233,14.7855 13.7727,13.7602 14.6518,12.4446C15.5308,11.129 16,9.5822 16,8C16.0031,6.9525 15.7998,5.9146 15.4017,4.9457C15.0037,3.9767 14.4187,3.0956 13.6801,2.3528C12.9416,1.6099 12.0639,1.0198 11.0973,0.6161C10.1307,0.2124 9.094,0.003 8.0465,0H8ZM8.1356,8.2712L7.3027,11.0799H11.7579C11.7874,11.0789 11.8168,11.0837 11.8445,11.094C11.8721,11.1044 11.8975,11.12 11.9191,11.1402C11.9407,11.1603 11.9581,11.1846 11.9703,11.2114C11.9825,11.2383 11.9893,11.2673 11.9903,11.2969V11.3705L11.6029,12.707C11.5858,12.7702 11.5478,12.8257 11.495,12.8645C11.4422,12.9032 11.3778,12.9228 11.3124,12.9201H4.4939L5.6368,9.0266L4.3584,9.414L4.6489,8.523L5.9274,8.1356L7.5351,2.6731C7.5528,2.6103 7.591,2.5552 7.6437,2.5165C7.6963,2.4779 7.7604,2.458 7.8257,2.4601H9.5496C9.5792,2.459 9.6086,2.4638 9.6362,2.4741C9.6639,2.4845 9.6892,2.5002 9.7108,2.5203C9.7324,2.5405 9.7498,2.5647 9.762,2.5916C9.7743,2.6185 9.7811,2.6475 9.7821,2.677V2.7506L8.4262,7.3608L9.7046,6.9734L9.4334,7.9032L8.1356,8.2712Z"/>
|
||||
</group>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_litecoin_round.xml
Normal file
9
app/src/main/res/drawable/ic_litecoin_round.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group>
|
||||
<clip-path android:pathData="M0,0h16v16h-16z"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M8,16C12.4183,16 16,12.4183 16,8C16,3.5817 12.4183,0 8,0C3.5817,0 0,3.5817 0,8C0,12.4183 3.5817,16 8,16Z"/>
|
||||
<path android:fillColor="#345D9D" android:pathData="M8,0C6.4177,0 4.871,0.4692 3.5554,1.3482C2.2399,2.2273 1.2145,3.4767 0.609,4.9385C0.0035,6.4003 -0.155,8.0089 0.1537,9.5607C0.4624,11.1126 1.2243,12.538 2.3431,13.6569C3.462,14.7757 4.8874,15.5376 6.4393,15.8463C7.9911,16.155 9.5997,15.9965 11.0615,15.391C12.5233,14.7855 13.7727,13.7602 14.6518,12.4446C15.5308,11.129 16,9.5822 16,8C16.0031,6.9525 15.7998,5.9146 15.4017,4.9457C15.0037,3.9767 14.4187,3.0956 13.6801,2.3528C12.9416,1.6099 12.0639,1.0198 11.0973,0.6161C10.1307,0.2124 9.094,0.003 8.0465,0H8ZM8.1356,8.2712L7.3027,11.0799H11.7579C11.7874,11.0789 11.8168,11.0837 11.8445,11.094C11.8721,11.1044 11.8975,11.12 11.9191,11.1402C11.9407,11.1603 11.9581,11.1846 11.9703,11.2114C11.9825,11.2383 11.9893,11.2673 11.9903,11.2969V11.3705L11.6029,12.707C11.5858,12.7702 11.5478,12.8257 11.495,12.8645C11.4422,12.9032 11.3778,12.9228 11.3124,12.9201H4.4939L5.6368,9.0266L4.3584,9.414L4.6489,8.523L5.9274,8.1356L7.5351,2.6731C7.5528,2.6103 7.591,2.5552 7.6437,2.5165C7.6963,2.4779 7.7604,2.458 7.8257,2.4601H9.5496C9.5792,2.459 9.6086,2.4638 9.6362,2.4741C9.6639,2.4845 9.6892,2.5002 9.7108,2.5203C9.7324,2.5405 9.7498,2.5647 9.762,2.5916C9.7743,2.6185 9.7811,2.6475 9.7821,2.677V2.7506L8.4262,7.3608L9.7046,6.9734L9.4334,7.9032L8.1356,8.2712Z"/>
|
||||
</group>
|
||||
</vector>
|
||||
11
app/src/main/res/drawable/ic_polkadot_no_color.xml
Normal file
11
app/src/main/res/drawable/ic_polkadot_no_color.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M7.9994,4.5169C9.2703,4.5169 10.3006,3.9535 10.3006,3.2585C10.3006,2.5634 9.2703,2 7.9994,2C6.7285,2 5.6982,2.5634 5.6982,3.2585C5.6982,3.9535 6.7285,4.5169 7.9994,4.5169Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M7.9994,13.9991C9.2703,13.9991 10.3006,13.4357 10.3006,12.7406C10.3006,12.0456 9.2703,11.4822 7.9994,11.4822C6.7285,11.4822 5.6982,12.0456 5.6982,12.7406C5.6982,13.4357 6.7285,13.9991 7.9994,13.9991Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M4.7927,6.259C5.4282,5.2241 5.424,4.1032 4.7835,3.7555C4.1429,3.4077 3.1086,3.9648 2.4731,4.9997C1.8376,6.0346 1.8418,7.1554 2.4823,7.5032C3.1228,7.8509 4.1572,7.2939 4.7927,6.259Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M13.5251,10.9994C14.1605,9.9645 14.1567,8.8438 13.5166,8.4963C12.8765,8.1488 11.8425,8.706 11.207,9.7409C10.5715,10.7758 10.5753,11.8965 11.2154,12.244C11.8555,12.5915 12.8896,12.0343 13.5251,10.9994Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M4.7837,12.2438C5.4243,11.896 5.4284,10.7752 4.793,9.7403C4.1575,8.7054 3.1231,8.1483 2.4826,8.4961C1.842,8.8438 1.8379,9.9647 2.4733,10.9996C3.1088,12.0345 4.1432,12.5915 4.7837,12.2438Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M13.517,7.5035C14.1571,7.156 14.1609,6.0353 13.5255,5.0004C12.89,3.9655 11.856,3.4083 11.2159,3.7558C10.5758,4.1033 10.572,5.224 11.2075,6.2589C11.8429,7.2938 12.8769,7.851 13.517,7.5035Z"/>
|
||||
</vector>
|
||||
11
app/src/main/res/drawable/ic_polkadot_round.xml
Normal file
11
app/src/main/res/drawable/ic_polkadot_round.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FEF2F8" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#E6007A" android:pathData="M7.9994,4.5169C9.2703,4.5169 10.3006,3.9535 10.3006,3.2585C10.3006,2.5634 9.2703,2 7.9994,2C6.7285,2 5.6982,2.5634 5.6982,3.2585C5.6982,3.9535 6.7285,4.5169 7.9994,4.5169Z"/>
|
||||
<path android:fillColor="#E6007A" android:pathData="M7.9994,13.9991C9.2703,13.9991 10.3006,13.4357 10.3006,12.7406C10.3006,12.0456 9.2703,11.4822 7.9994,11.4822C6.7285,11.4822 5.6982,12.0456 5.6982,12.7406C5.6982,13.4357 6.7285,13.9991 7.9994,13.9991Z"/>
|
||||
<path android:fillColor="#E6007A" android:pathData="M4.7927,6.259C5.4282,5.2241 5.424,4.1032 4.7835,3.7555C4.1429,3.4077 3.1086,3.9648 2.4731,4.9997C1.8376,6.0346 1.8418,7.1554 2.4823,7.5032C3.1228,7.8509 4.1572,7.2939 4.7927,6.259Z"/>
|
||||
<path android:fillColor="#E6007A" android:pathData="M13.5251,10.9994C14.1605,9.9645 14.1567,8.8438 13.5166,8.4963C12.8765,8.1488 11.8425,8.706 11.207,9.7409C10.5715,10.7758 10.5753,11.8965 11.2154,12.244C11.8555,12.5915 12.8896,12.0343 13.5251,10.9994Z"/>
|
||||
<path android:fillColor="#E6007A" android:pathData="M4.7837,12.2438C5.4243,11.896 5.4284,10.7752 4.793,9.7403C4.1575,8.7054 3.1231,8.1483 2.4826,8.4961C1.842,8.8438 1.8379,9.9647 2.4733,10.9996C3.1088,12.0345 4.1432,12.5915 4.7837,12.2438Z"/>
|
||||
<path android:fillColor="#E6007A" android:pathData="M13.517,7.5035C14.1571,7.156 14.1609,6.0353 13.5255,5.0004C12.89,3.9655 11.856,3.4083 11.2159,3.7558C10.5758,4.1033 10.572,5.224 11.2075,6.2589C11.8429,7.2938 12.8769,7.851 13.517,7.5035Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_polygon_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_polygon_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,16C12.4183,16 16,12.4183 16,8C16,3.5817 12.4183,0 8,0C3.5817,0 0,3.5817 0,8C0,12.4183 3.5817,16 8,16Z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M10.6283,6.3937C10.4378,6.2889 10.1902,6.2889 9.9808,6.3937L8.4952,7.1968L7.4858,7.7205L6.0003,8.5237C5.8098,8.6284 5.5622,8.6284 5.3527,8.5237L4.1719,7.8951C3.9815,7.7904 3.8482,7.5983 3.8482,7.3888V6.1492C3.8482,5.9397 3.9624,5.7477 4.1719,5.6429L5.3337,5.0319C5.5241,4.9271 5.7717,4.9271 5.9812,5.0319L7.143,5.6429C7.3335,5.7477 7.4668,5.9397 7.4668,6.1492V6.9524L8.4762,6.4111V5.608C8.4762,5.3985 8.3619,5.2064 8.1524,5.1017L6.0003,3.9494C5.8098,3.8447 5.5622,3.8447 5.3527,3.9494L3.1625,5.1017C2.953,5.2064 2.8387,5.3985 2.8387,5.608V7.93C2.8387,8.1395 2.953,8.3316 3.1625,8.4363L5.3527,9.5886C5.5432,9.6934 5.7908,9.6934 6.0003,9.5886L7.4858,8.803L8.4952,8.2618L9.9808,7.4761C10.1712,7.3714 10.4188,7.3714 10.6283,7.4761L11.7901,8.0872C11.9805,8.1919 12.1138,8.384 12.1138,8.5935V9.8331C12.1138,10.0426 11.9996,10.2346 11.7901,10.3394L10.6283,10.9679C10.4378,11.0727 10.1902,11.0727 9.9808,10.9679L8.819,10.3568C8.6285,10.2521 8.4952,10.06 8.4952,9.8505V9.0474L7.4858,9.5886V10.3917C7.4858,10.6013 7.6001,10.7933 7.8096,10.8981L9.9998,12.0504C10.1902,12.1551 10.4378,12.1551 10.6473,12.0504L12.8376,10.8981C13.028,10.7933 13.1613,10.6013 13.1613,10.3917V8.0697C13.1613,7.8602 13.0471,7.6682 12.8376,7.5634L10.6283,6.3937Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_polygon_round.xml
Normal file
6
app/src/main/res/drawable/ic_polygon_round.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#8247E5" android:pathData="M8,16C12.4183,16 16,12.4183 16,8C16,3.5817 12.4183,0 8,0C3.5817,0 0,3.5817 0,8C0,12.4183 3.5817,16 8,16Z"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M10.6283,6.3937C10.4378,6.2889 10.1902,6.2889 9.9808,6.3937L8.4952,7.1968L7.4858,7.7205L6.0003,8.5237C5.8098,8.6284 5.5622,8.6284 5.3527,8.5237L4.1719,7.8951C3.9815,7.7904 3.8482,7.5983 3.8482,7.3888V6.1492C3.8482,5.9397 3.9624,5.7477 4.1719,5.6429L5.3337,5.0319C5.5241,4.9271 5.7717,4.9271 5.9812,5.0319L7.143,5.6429C7.3335,5.7477 7.4668,5.9397 7.4668,6.1492V6.9524L8.4762,6.4111V5.608C8.4762,5.3985 8.3619,5.2064 8.1524,5.1017L6.0003,3.9494C5.8098,3.8447 5.5622,3.8447 5.3527,3.9494L3.1625,5.1017C2.953,5.2064 2.8387,5.3985 2.8387,5.608V7.93C2.8387,8.1395 2.953,8.3316 3.1625,8.4363L5.3527,9.5886C5.5432,9.6934 5.7908,9.6934 6.0003,9.5886L7.4858,8.803L8.4952,8.2618L9.9808,7.4761C10.1712,7.3714 10.4188,7.3714 10.6283,7.4761L11.7901,8.0872C11.9805,8.1919 12.1138,8.384 12.1138,8.5935V9.8331C12.1138,10.0426 11.9996,10.2346 11.7901,10.3394L10.6283,10.9679C10.4378,11.0727 10.1902,11.0727 9.9808,10.9679L8.819,10.3568C8.6285,10.2521 8.4952,10.06 8.4952,9.8505V9.0474L7.4858,9.5886V10.3917C7.4858,10.6013 7.6001,10.7933 7.8096,10.8981L9.9998,12.0504C10.1902,12.1551 10.4378,12.1551 10.6473,12.0504L12.8376,10.8981C13.028,10.7933 13.1613,10.6013 13.1613,10.3917V8.0697C13.1613,7.8602 13.0471,7.6682 12.8376,7.5634L10.6283,6.3937Z"/>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_rsk_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_rsk_no_color.xml
Normal file
File diff suppressed because one or more lines are too long
6
app/src/main/res/drawable/ic_rsk_round.xml
Normal file
6
app/src/main/res/drawable/ic_rsk_round.xml
Normal file
File diff suppressed because one or more lines are too long
6
app/src/main/res/drawable/ic_solana_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_solana_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,0L8,0A8,8 0,0 1,16 8L16,8A8,8 0,0 1,8 16L8,16A8,8 0,0 1,0 8L0,8A8,8 0,0 1,8 0z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M12.3419,10.4366L10.8935,12.0202C10.862,12.0546 10.8239,12.082 10.7816,12.1008C10.7392,12.1195 10.6936,12.1292 10.6474,12.1292H3.7811C3.7483,12.1292 3.7163,12.1194 3.6889,12.1011C3.6615,12.0828 3.6399,12.0567 3.6268,12.0261C3.6138,11.9954 3.6098,11.9616 3.6154,11.9287C3.6209,11.8957 3.6358,11.8652 3.6581,11.8408L5.1077,10.2572C5.1391,10.2228 5.177,10.1955 5.2192,10.1767C5.2614,10.158 5.307,10.1483 5.353,10.1482H12.219C12.2517,10.1482 12.2838,10.158 12.3112,10.1763C12.3385,10.1946 12.3601,10.2207 12.3732,10.2513C12.3862,10.2819 12.3902,10.3158 12.3847,10.3487C12.3791,10.3816 12.3643,10.4122 12.3419,10.4366ZM10.8935,7.2476C10.862,7.2132 10.8239,7.1858 10.7816,7.167C10.7392,7.1483 10.6936,7.1386 10.6474,7.1387H3.7811C3.7483,7.1387 3.7163,7.1484 3.6889,7.1667C3.6615,7.1851 3.6399,7.2111 3.6268,7.2418C3.6138,7.2724 3.6098,7.3063 3.6154,7.3392C3.6209,7.3721 3.6358,7.4026 3.6581,7.4271L5.1077,9.0107C5.1391,9.045 5.177,9.0723 5.2192,9.0911C5.2614,9.1098 5.307,9.1195 5.353,9.1196H12.219C12.2517,9.1196 12.2838,9.1099 12.3112,9.0915C12.3385,9.0732 12.3601,9.0472 12.3732,9.0165C12.3862,8.9859 12.3902,8.952 12.3847,8.9191C12.3791,8.8862 12.3643,8.8557 12.3419,8.8312L10.8935,7.2476ZM3.7811,6.1101H10.6474C10.6936,6.1101 10.7392,6.1005 10.7816,6.0817C10.8239,6.063 10.862,6.0356 10.8935,6.0012L12.3419,4.4175C12.3643,4.3931 12.3791,4.3626 12.3847,4.3296C12.3902,4.2967 12.3862,4.2629 12.3732,4.2323C12.3601,4.2016 12.3385,4.1755 12.3112,4.1572C12.2838,4.1389 12.2517,4.1291 12.219,4.1291H5.353C5.307,4.1292 5.2614,4.1389 5.2192,4.1577C5.177,4.1764 5.1391,4.2038 5.1077,4.2381L3.6585,5.8217C3.6362,5.8461 3.6213,5.8766 3.6157,5.9095C3.6102,5.9424 3.6141,5.9762 3.6272,6.0068C3.6402,6.0374 3.6617,6.0635 3.689,6.0819C3.7163,6.1002 3.7483,6.11 3.7811,6.1101Z"/>
|
||||
</vector>
|
||||
18
app/src/main/res/drawable/ic_solana_round.xml
Normal file
18
app/src/main/res/drawable/ic_solana_round.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#000000" android:pathData="M8,0L8,0A8,8 0,0 1,16 8L16,8A8,8 0,0 1,8 16L8,16A8,8 0,0 1,0 8L0,8A8,8 0,0 1,8 0z"/>
|
||||
<path android:pathData="M12.3419,10.4366L10.8935,12.0202C10.862,12.0546 10.8239,12.082 10.7816,12.1008C10.7392,12.1195 10.6936,12.1292 10.6474,12.1292H3.7811C3.7483,12.1292 3.7163,12.1194 3.6889,12.1011C3.6615,12.0828 3.6399,12.0567 3.6268,12.0261C3.6138,11.9954 3.6098,11.9616 3.6154,11.9287C3.6209,11.8957 3.6358,11.8652 3.6581,11.8408L5.1077,10.2572C5.1391,10.2228 5.177,10.1955 5.2192,10.1767C5.2614,10.158 5.307,10.1483 5.353,10.1482H12.219C12.2517,10.1482 12.2838,10.158 12.3112,10.1763C12.3385,10.1946 12.3601,10.2207 12.3732,10.2513C12.3862,10.2819 12.3902,10.3158 12.3847,10.3487C12.3791,10.3816 12.3643,10.4122 12.3419,10.4366ZM10.8935,7.2476C10.862,7.2132 10.8239,7.1858 10.7816,7.167C10.7392,7.1483 10.6936,7.1386 10.6474,7.1387H3.7811C3.7483,7.1387 3.7163,7.1484 3.6889,7.1667C3.6615,7.1851 3.6399,7.2111 3.6268,7.2418C3.6138,7.2724 3.6098,7.3063 3.6154,7.3392C3.6209,7.3721 3.6358,7.4026 3.6581,7.4271L5.1077,9.0107C5.1391,9.045 5.177,9.0723 5.2192,9.0911C5.2614,9.1098 5.307,9.1195 5.353,9.1196H12.219C12.2517,9.1196 12.2838,9.1099 12.3112,9.0915C12.3385,9.0732 12.3601,9.0472 12.3732,9.0165C12.3862,8.9859 12.3902,8.952 12.3847,8.9191C12.3791,8.8862 12.3643,8.8557 12.3419,8.8312L10.8935,7.2476ZM3.7811,6.1101H10.6474C10.6936,6.1101 10.7392,6.1005 10.7816,6.0817C10.8239,6.063 10.862,6.0356 10.8935,6.0012L12.3419,4.4175C12.3643,4.3931 12.3791,4.3626 12.3847,4.3296C12.3902,4.2967 12.3862,4.2629 12.3732,4.2323C12.3601,4.2016 12.3385,4.1755 12.3112,4.1572C12.2838,4.1389 12.2517,4.1291 12.219,4.1291H5.353C5.307,4.1292 5.2614,4.1389 5.2192,4.1577C5.177,4.1764 5.1391,4.2038 5.1077,4.2381L3.6585,5.8217C3.6362,5.8461 3.6213,5.8766 3.6157,5.9095C3.6102,5.9424 3.6141,5.9762 3.6272,6.0068C3.6402,6.0374 3.6617,6.0635 3.689,6.0819C3.7163,6.1002 3.7483,6.11 3.7811,6.1101Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient android:endX="11.7097" android:endY="4.18558"
|
||||
android:startX="4.35356" android:startY="12.3198" android:type="linear">
|
||||
<item android:color="#FF9945FF" android:offset="0.08"/>
|
||||
<item android:color="#FF8752F3" android:offset="0.3"/>
|
||||
<item android:color="#FF5497D5" android:offset="0.5"/>
|
||||
<item android:color="#FF43B4CA" android:offset="0.6"/>
|
||||
<item android:color="#FF28E0B9" android:offset="0.72"/>
|
||||
<item android:color="#FF19FB9B" android:offset="0.97"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/ic_stellar_no_color.xml
Normal file
12
app/src/main/res/drawable/ic_stellar_no_color.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group>
|
||||
<clip-path android:pathData="M0,0h16v16h-16z"/>
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,0L8,0A8,8 0,0 1,16 8L16,8A8,8 0,0 1,8 16L8,16A8,8 0,0 1,0 8L0,8A8,8 0,0 1,8 0z"/>
|
||||
<group>
|
||||
<clip-path android:pathData="M7.871,2.8386L7.871,2.8386A5.0322,5.0322 0,0 1,12.9032 7.8709L12.9032,7.8709A5.0322,5.0322 0,0 1,7.871 12.9031L7.871,12.9031A5.0322,5.0322 0,0 1,2.8387 7.8709L2.8387,7.8709A5.0322,5.0322 0,0 1,7.871 2.8386z"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M7.9897,3.6149C7.4207,3.5991 6.8543,3.6974 6.324,3.9042C5.7937,4.111 5.3102,4.4221 4.9022,4.8189C4.4941,5.2158 4.1698,5.6904 3.9483,6.2148C3.7269,6.7392 3.6128,7.3026 3.6129,7.8718C3.6129,7.9804 3.6171,8.0882 3.6254,8.196C3.637,8.3484 3.6031,8.5007 3.5282,8.6339C3.4533,8.7671 3.3406,8.8752 3.2044,8.9445L2.8387,9.1311V10.0009L3.9156,9.4519L4.2646,9.2741L4.6084,9.0993L10.7771,5.9558L11.4703,5.6027L12.9033,4.8726V4.0028L11.4829,4.7275L10.271,5.3452L4.4189,8.326C4.3992,8.175 4.3894,8.0228 4.3895,7.8706C4.3903,7.2632 4.5498,6.6667 4.8521,6.1399C5.1544,5.6132 5.589,5.1746 6.113,4.8675C6.637,4.5605 7.2321,4.3956 7.8393,4.3893C8.4466,4.3829 9.045,4.5354 9.5753,4.8315L10.2689,4.478L10.3725,4.4251C9.678,3.9211 8.8474,3.6388 7.9897,3.6149ZM12.9033,5.7427L4.9586,9.7878L4.2654,10.1418L2.8387,10.8694V11.7383L4.2553,11.0161L5.4673,10.3984L11.3256,7.4139C11.3453,7.5657 11.3551,7.7187 11.355,7.8718C11.3546,8.4798 11.1952,9.0771 10.8926,9.6045C10.59,10.1319 10.1548,10.5709 9.6301,10.8781C9.1054,11.1853 8.5094,11.3498 7.9015,11.3555C7.2935,11.3612 6.6946,11.2078 6.1642,10.9105L6.1219,10.9331L5.37,11.3164C6.0053,11.7776 6.7557,12.0541 7.5383,12.1155C8.3209,12.177 9.1053,12.0208 9.8048,11.6644C10.5042,11.308 11.0915,10.7651 11.5018,10.0958C11.912,9.4265 12.1293,8.6568 12.1296,7.8718C12.1296,7.7628 12.1254,7.6529 12.117,7.5447C12.1055,7.3924 12.1393,7.24 12.2141,7.1069C12.289,6.9737 12.4015,6.8656 12.5376,6.7962L12.9033,6.6095V5.7427Z"/>
|
||||
</group>
|
||||
</group>
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/ic_stellar_round.xml
Normal file
12
app/src/main/res/drawable/ic_stellar_round.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group>
|
||||
<clip-path android:pathData="M0,0h16v16h-16z"/>
|
||||
<path android:fillColor="#000000" android:pathData="M8,0L8,0A8,8 0,0 1,16 8L16,8A8,8 0,0 1,8 16L8,16A8,8 0,0 1,0 8L0,8A8,8 0,0 1,8 0z"/>
|
||||
<group>
|
||||
<clip-path android:pathData="M7.871,2.8386L7.871,2.8386A5.0322,5.0322 0,0 1,12.9032 7.8709L12.9032,7.8709A5.0322,5.0322 0,0 1,7.871 12.9031L7.871,12.9031A5.0322,5.0322 0,0 1,2.8387 7.8709L2.8387,7.8709A5.0322,5.0322 0,0 1,7.871 2.8386z"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M7.9897,3.6149C7.4207,3.5991 6.8543,3.6974 6.324,3.9042C5.7937,4.111 5.3102,4.4221 4.9022,4.8189C4.4941,5.2158 4.1698,5.6904 3.9483,6.2148C3.7269,6.7392 3.6128,7.3026 3.6129,7.8718C3.6129,7.9804 3.6171,8.0882 3.6254,8.196C3.637,8.3484 3.6031,8.5007 3.5282,8.6339C3.4533,8.7671 3.3406,8.8752 3.2044,8.9445L2.8387,9.1311V10.0009L3.9156,9.4519L4.2646,9.2741L4.6084,9.0993L10.7771,5.9558L11.4703,5.6027L12.9033,4.8726V4.0028L11.4829,4.7275L10.271,5.3452L4.4189,8.326C4.3992,8.175 4.3894,8.0228 4.3895,7.8706C4.3903,7.2632 4.5498,6.6667 4.8521,6.1399C5.1544,5.6132 5.589,5.1746 6.113,4.8675C6.637,4.5605 7.2321,4.3956 7.8393,4.3893C8.4466,4.3829 9.045,4.5354 9.5753,4.8315L10.2689,4.478L10.3725,4.4251C9.678,3.9211 8.8474,3.6388 7.9897,3.6149ZM12.9033,5.7427L4.9586,9.7878L4.2654,10.1418L2.8387,10.8694V11.7383L4.2553,11.0161L5.4673,10.3984L11.3256,7.4139C11.3453,7.5657 11.3551,7.7187 11.355,7.8718C11.3546,8.4798 11.1952,9.0771 10.8926,9.6045C10.59,10.1319 10.1548,10.5709 9.6301,10.8781C9.1054,11.1853 8.5094,11.3498 7.9015,11.3555C7.2935,11.3612 6.6946,11.2078 6.1642,10.9105L6.1219,10.9331L5.37,11.3164C6.0053,11.7776 6.7557,12.0541 7.5383,12.1155C8.3209,12.177 9.1053,12.0208 9.8048,11.6644C10.5042,11.308 11.0915,10.7651 11.5018,10.0958C11.912,9.4265 12.1293,8.6568 12.1296,7.8718C12.1296,7.7628 12.1254,7.6529 12.117,7.5447C12.1055,7.3924 12.1393,7.24 12.2141,7.1069C12.289,6.9737 12.4015,6.8656 12.5376,6.7962L12.9033,6.6095V5.7427Z"/>
|
||||
</group>
|
||||
</group>
|
||||
</vector>
|
||||
6
app/src/main/res/drawable/ic_tezos_no_color.xml
Normal file
6
app/src/main/res/drawable/ic_tezos_no_color.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:autoMirrored="true" android:height="16dp"
|
||||
android:viewportHeight="16" android:viewportWidth="16"
|
||||
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#F1F1F1" android:pathData="M8,8m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"/>
|
||||
<path android:fillColor="#A1A1A4" android:pathData="M8.5216,13C7.8414,13 7.345,12.8281 7.0322,12.4844C6.7425,12.1934 6.5744,11.7939 6.5646,11.3732C6.5611,11.2455 6.5893,11.1189 6.6465,11.006C6.6999,10.9085 6.777,10.8275 6.8699,10.7715C6.9779,10.715 7.097,10.6856 7.2177,10.6856C7.3384,10.6856 7.4574,10.715 7.5654,10.7715C7.6582,10.8273 7.7349,10.9083 7.7875,11.006C7.8464,11.1184 7.8757,11.2451 7.8724,11.3732C7.8784,11.5263 7.8366,11.6772 7.7532,11.8029C7.6838,11.9056 7.5844,11.9818 7.4702,12.0203C7.5819,12.1695 7.74,12.2729 7.917,12.3125C8.1149,12.3728 8.3199,12.4038 8.5261,12.4046C8.799,12.4071 9.0663,12.3233 9.2931,12.164C9.5284,11.9896 9.7038,11.7403 9.7935,11.4531C9.9071,11.1112 9.9626,10.7511 9.9573,10.3891C9.9665,10.0117 9.9058,9.636 9.7786,9.2828C9.6829,9.0003 9.5013,8.7587 9.2618,8.5953C9.0392,8.4475 8.7807,8.3699 8.5173,8.3719C8.2877,8.3923 8.0663,8.4711 7.8724,8.6016L7.3942,8.8531V8.6016L9.5433,5.5769H6.5646V8.7156C6.5562,8.9421 6.6133,9.166 6.7284,9.3578C6.7842,9.4414 6.8595,9.5085 6.9472,9.5525C7.0348,9.5966 7.1319,9.6162 7.2288,9.6093C7.4081,9.6055 7.5814,9.5415 7.7233,9.4265C7.8841,9.3029 8.0241,9.1518 8.1373,8.9796C8.1475,8.9495 8.1663,8.9233 8.1912,8.9047C8.2111,8.8866 8.2366,8.8766 8.263,8.8766C8.3147,8.8815 8.3637,8.9034 8.403,8.939C8.4528,8.9974 8.4798,9.0732 8.4789,9.1515C8.4732,9.2042 8.4642,9.2563 8.4521,9.3078C8.3463,9.5765 8.1689,9.8075 7.9412,9.9732C7.7298,10.1233 7.48,10.2029 7.2247,10.2016C6.5798,10.2016 6.133,10.0682 5.8844,9.8016C5.7566,9.6542 5.6581,9.4815 5.5947,9.2937C5.5312,9.1059 5.5042,8.9067 5.515,8.7078V5.5769H4V4.9937H5.5221V3.664L5.1736,3.2982V3H6.1848L6.5646,3.2063V4.9937L10.4993,4.9812L10.8911,5.3937L8.4784,7.9372C8.6241,7.8757 8.7776,7.8368 8.9341,7.8216C9.2437,7.8358 9.5458,7.9263 9.8159,8.0857C10.1287,8.2417 10.3923,8.4885 10.5755,8.7969C10.7396,9.0579 10.8558,9.3488 10.9181,9.6547C10.9692,9.8959 10.9967,10.142 11,10.3891C11.0016,10.8607 10.8997,11.3264 10.7021,11.75C10.5148,12.1602 10.1995,12.491 9.8085,12.6876C9.4082,12.8945 8.9678,13.0014 8.5216,13Z"/>
|
||||
</vector>
|
||||
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