Cw 451 wallet connect for ethereum (#1049)

* Update Flutter Update packages * Feat: Wallet connect for ethereum * Fix localization issues Fix UI issues Update old packages Update workflow Update how to build guide * feat: Wallet connect * feat: Add wallet connect for ethereum * chore: Add eth dependencies in configure file * Minor: `WalletConnect` settings name, not `Wallet connect` * fix: Merge conflicts * fix: Issues with test cases on various dApps, introduce Arbitrum rinkerby as suported chain * ui: Design fixes for WalletConnect flow * chore: Update repo and comment out send apk to channel in workflow * fix: Core implementation * feat: WalletConnect WIP * feat: WalletConnect WIP * feat: WalletConnect WIP * chore: Unused parameters WIP [skip ci] * fix: Code review fixes * Feat: WalletConnect feat WIP * feat: WalletConnect * feat: WalletConnect * feat: WalletConnect * Feat: WalletConnect * Feat: WalletConnect * feat: Remove queue support for the bottomsheet * feat: WalletConnect feature, bug fixes, folder restructuring, localization * Feat: Add positive feedback prompt on successful transaction * fix: Delete session bug * fix: dependencies registration WIP * feat: Registering dependencies for walletconnect * chore: Move key data to secrets * chore: ensure appropriate null checks * chore: localization * chore: Remove unused code * localization * chore: Remove unused code * chore: Remove unused code * chore: Add walletconnect project id key entry * fix: Revert bash command for linnux support * fix: Issues with translation in some languages and making unneeded external variable private * fix: Add bottomsheet listener to desktop dashboard page * Generalize ethereum not enough gas error check --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> Co-authored-by: Justin Ehrenhofer <justin.ehrenhofer@gmail.com>

Adegoke David committed Oct 3, 2023 at 15:56 UTC 32643823e58fd634a7180aa7ea59ffe7b01f02b7
67 files changed +2821 -316
.github/workflows/pr_test_build.yml
+17 -17
@@ -2,11 +2,10 @@ name: PR Test Build
2
3 on:
4 pull_request:
5 - branches: [ main ]
5 + branches: [main]
6
7 jobs:
8 PR_test_build:
9 -
9 runs-on: ubuntu-20.04
10 env:
11 STORE_PASS: test@cake_wallet
@@ -23,12 +22,12 @@ jobs:
22 - uses: actions/checkout@v2
23 - uses: actions/setup-java@v1
24 with:
26 - java-version: '8.x'
25 + java-version: "8.x"
26
27 - name: Flutter action
28 uses: subosito/flutter-action@v1
29 with:
31 - flutter-version: '3.10.x'
30 + flutter-version: "3.10.x"
31 channel: stable
32
33 - name: Install package dependencies
@@ -131,6 +130,7 @@ jobs:
130 echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart
131 echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
132 echo "const robinhoodCIdApiSecret = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
133 + echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart
134
135 - name: Rename app
136 run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties
@@ -140,18 +140,18 @@ jobs:
140 cd /opt/android/cake_wallet
141 flutter build apk --release
142
143 -# - name: Push to App Center
144 -# run: |
145 -# echo 'Installing App Center CLI tools'
146 -# npm install -g appcenter-cli
147 -# echo "Publishing test to App Center"
148 -# appcenter distribute release \
149 -# --group "Testers" \
150 -# --file "/opt/android/cake_wallet/build/app/outputs/apk/release/app-release.apk" \
151 -# --release-notes ${GITHUB_HEAD_REF} \
152 -# --app Cake-Labs/Cake-Wallet \
153 -# --token ${{ secrets.APP_CENTER_TOKEN }} \
154 -# --quiet
143 + # - name: Push to App Center
144 + # run: |
145 + # echo 'Installing App Center CLI tools'
146 + # npm install -g appcenter-cli
147 + # echo "Publishing test to App Center"
148 + # appcenter distribute release \
149 + # --group "Testers" \
150 + # --file "/opt/android/cake_wallet/build/app/outputs/apk/release/app-release.apk" \
151 + # --release-notes ${GITHUB_HEAD_REF} \
152 + # --app Cake-Labs/Cake-Wallet \
153 + # --token ${{ secrets.APP_CENTER_TOKEN }} \
154 + # --quiet
155
156 - name: Rename apk file
157 run: |
@@ -171,6 +171,6 @@ jobs:
171 token: ${{ secrets.SLACK_APP_TOKEN }}
172 path: /opt/android/cake_wallet/build/app/outputs/apk/release/app-release.apk
173 channel: ${{ secrets.SLACK_APK_CHANNEL }}
174 - title: '${{github.head_ref}}.apk'
174 + title: "${{github.head_ref}}.apk"
175 filename: ${{github.head_ref}}.apk
176 initial_comment: ${{ github.event.head_commit.message }}
assets/images/walletconnect_logo.png
Binary files /dev/null and b/assets/images/walletconnect_logo.png differ
cw_ethereum/lib/ethereum_wallet.dart
+2
@@ -77,6 +77,8 @@ abstract class EthereumWalletBase
77
78 late final EthPrivateKey _ethPrivateKey;
79
80 + EthPrivateKey get ethPrivateKey => _ethPrivateKey;
81 +
82 late EthereumClient _client;
83
84 int? _gasPrice;
lib/core/wallet_connect/chain_service.dart new
+5
@@ -0,0 +1,5 @@
1 +abstract class ChainService {
2 + String getNamespace();
3 + String getChainId();
4 + List<String> getEvents();
5 +}
lib/core/wallet_connect/eth_transaction_model.dart new
+60
@@ -0,0 +1,60 @@
1 +class WCEthereumTransactionModel {
2 + final String from;
3 + final String to;
4 + final String value;
5 + final String? nonce;
6 + final String? gasPrice;
7 + final String? maxFeePerGas;
8 + final String? maxPriorityFeePerGas;
9 + final String? gas;
10 + final String? gasLimit;
11 + final String? data;
12 +
13 + WCEthereumTransactionModel({
14 + required this.from,
15 + required this.to,
16 + required this.value,
17 + this.nonce,
18 + this.gasPrice,
19 + this.maxFeePerGas,
20 + this.maxPriorityFeePerGas,
21 + this.gas,
22 + this.gasLimit,
23 + this.data,
24 + });
25 +
26 + factory WCEthereumTransactionModel.fromJson(Map<String, dynamic> json) {
27 + return WCEthereumTransactionModel(
28 + from: json['from'] as String,
29 + to: json['to'] as String,
30 + value: json['value'] as String,
31 + nonce: json['nonce'] as String?,
32 + gasPrice: json['gasPrice'] as String?,
33 + maxFeePerGas: json['maxFeePerGas'] as String?,
34 + maxPriorityFeePerGas: json['maxPriorityFeePerGas'] as String?,
35 + gas: json['gas'] as String?,
36 + gasLimit: json['gasLimit'] as String?,
37 + data: json['data'] as String?,
38 + );
39 + }
40 +
41 + Map<String, dynamic> toJson() {
42 + return {
43 + 'from': from,
44 + 'to': to,
45 + 'value': value,
46 + 'nonce': nonce,
47 + 'gasPrice': gasPrice,
48 + 'maxFeePerGas': maxFeePerGas,
49 + 'maxPriorityFeePerGas': maxPriorityFeePerGas,
50 + 'gas': gas,
51 + 'gasLimit': gasLimit,
52 + 'data': data,
53 + };
54 + }
55 +
56 + @override
57 + String toString() {
58 + return 'EthereumTransactionModel(from: $from, to: $to, nonce: $nonce, gasPrice: $gasPrice, maxFeePerGas: $maxFeePerGas, maxPriorityFeePerGas: $maxPriorityFeePerGas, gas: $gas, gasLimit: $gasLimit, value: $value, data: $data)';
59 + }
60 +}
lib/core/wallet_connect/evm_chain_id.dart new
+35
@@ -0,0 +1,35 @@
1 +import 'package:cake_wallet/core/wallet_connect/evm_chain_service.dart';
2 +
3 +enum EVMChainId {
4 + ethereum,
5 + polygon,
6 + goerli,
7 + mumbai,
8 + arbitrum,
9 +}
10 +
11 +extension EVMChainIdX on EVMChainId {
12 + String chain() {
13 + String name = '';
14 +
15 + switch (this) {
16 + case EVMChainId.ethereum:
17 + name = '1';
18 + break;
19 + case EVMChainId.polygon:
20 + name = '137';
21 + break;
22 + case EVMChainId.goerli:
23 + name = '5';
24 + break;
25 + case EVMChainId.arbitrum:
26 + name = '42161';
27 + break;
28 + case EVMChainId.mumbai:
29 + name = '80001';
30 + break;
31 + }
32 +
33 + return '${EvmChainServiceImpl.namespace}:$name';
34 + }
35 +}
lib/core/wallet_connect/evm_chain_service.dart new
+294
@@ -0,0 +1,294 @@
1 +import 'dart:convert';
2 +import 'dart:developer';
3 +import 'dart:typed_data';
4 +
5 +import 'package:cake_wallet/core/wallet_connect/eth_transaction_model.dart';
6 +import 'package:cake_wallet/core/wallet_connect/evm_chain_id.dart';
7 +import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
8 +import 'package:cake_wallet/generated/i18n.dart';
9 +import 'package:cake_wallet/src/screens/wallet_connect/widgets/error_display_widget.dart';
10 +import 'package:cake_wallet/store/app_store.dart';
11 +import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart';
12 +import 'package:cake_wallet/core/wallet_connect/models/connection_model.dart';
13 +import 'package:cake_wallet/src/screens/wallet_connect/widgets/connection_widget.dart';
14 +import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/web3_request_modal.dart';
15 +import 'package:cake_wallet/src/screens/wallet_connect/utils/string_parsing.dart';
16 +import 'package:convert/convert.dart';
17 +import 'package:cw_core/wallet_type.dart';
18 +import 'package:eth_sig_util/eth_sig_util.dart';
19 +import 'package:eth_sig_util/util/utils.dart';
20 +import 'package:http/http.dart' as http;
21 +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
22 +import 'package:web3dart/web3dart.dart';
23 +import 'chain_service.dart';
24 +import 'wallet_connect_key_service.dart';
25 +
26 +class EvmChainServiceImpl implements ChainService {
27 + final AppStore appStore;
28 + final BottomSheetService bottomSheetService;
29 + final Web3Wallet wallet;
30 + final WalletConnectKeyService wcKeyService;
31 +
32 + static const namespace = 'eip155';
33 + static const pSign = 'personal_sign';
34 + static const eSign = 'eth_sign';
35 + static const eSignTransaction = 'eth_signTransaction';
36 + static const eSignTypedData = 'eth_signTypedData_v4';
37 + static const eSendTransaction = 'eth_sendTransaction';
38 +
39 + final EVMChainId reference;
40 +
41 + final Web3Client ethClient;
42 +
43 + EvmChainServiceImpl({
44 + required this.reference,
45 + required this.appStore,
46 + required this.wcKeyService,
47 + required this.bottomSheetService,
48 + required this.wallet,
49 + Web3Client? ethClient,
50 + }) : ethClient = ethClient ??
51 + Web3Client(
52 + appStore.settingsStore.getCurrentNode(WalletType.ethereum).uri.toString(),
53 + http.Client(),
54 + ) {
55 +
56 + for (final String event in getEvents()) {
57 + wallet.registerEventEmitter(chainId: getChainId(), event: event);
58 + }
59 + wallet.registerRequestHandler(
60 + chainId: getChainId(),
61 + method: pSign,
62 + handler: personalSign,
63 + );
64 + wallet.registerRequestHandler(
65 + chainId: getChainId(),
66 + method: eSign,
67 + handler: ethSign,
68 + );
69 + wallet.registerRequestHandler(
70 + chainId: getChainId(),
71 + method: eSignTransaction,
72 + handler: ethSignTransaction,
73 + );
74 + wallet.registerRequestHandler(
75 + chainId: getChainId(),
76 + method: eSendTransaction,
77 + handler: ethSignTransaction,
78 + );
79 + wallet.registerRequestHandler(
80 + chainId: getChainId(),
81 + method: eSignTypedData,
82 + handler: ethSignTypedData,
83 + );
84 + }
85 +
86 + @override
87 + String getNamespace() {
88 + return namespace;
89 + }
90 +
91 + @override
92 + String getChainId() {
93 + return reference.chain();
94 + }
95 +
96 + @override
97 + List<String> getEvents() {
98 + return ['chainChanged', 'accountsChanged'];
99 + }
100 +
101 + Future<String?> requestAuthorization(String? text) async {
102 + // Show the bottom sheet
103 + final bool? isApproved = await bottomSheetService.queueBottomSheet(
104 + widget: Web3RequestModal(
105 + child: ConnectionWidget(
106 + title: S.current.signTransaction,
107 + info: [
108 + ConnectionModel(
109 + text: text,
110 + ),
111 + ],
112 + ),
113 + ),
114 + ) as bool?;
115 +
116 + if (isApproved != null && isApproved == false) {
117 + return 'User rejected signature';
118 + }
119 +
120 + return null;
121 + }
122 +
123 + Future<String> personalSign(String topic, dynamic parameters) async {
124 + log('received personal sign request: $parameters');
125 +
126 + final String message;
127 + if (parameters[0] == null) {
128 + message = '';
129 + } else {
130 + message = parameters[0].toString().utf8Message;
131 + }
132 +
133 + final String? authError = await requestAuthorization(message);
134 +
135 + if (authError != null) {
136 + return authError;
137 + }
138 +
139 + try {
140 + // Load the private key
141 + final List<ChainKeyModel> keys = wcKeyService.getKeysForChain(getChainId());
142 +
143 + final Credentials credentials = EthPrivateKey.fromHex(keys[0].privateKey);
144 +
145 + final String signature = hex.encode(
146 + credentials.signPersonalMessageToUint8List(Uint8List.fromList(utf8.encode(message))),
147 + );
148 +
149 + return '0x$signature';
150 + } catch (e) {
151 + log(e.toString());
152 + bottomSheetService.queueBottomSheet(
153 + isModalDismissible: true,
154 + widget: BottomSheetMessageDisplayWidget(
155 + message: '${S.current.errorGettingCredentials} ${e.toString()}',
156 + ),
157 + );
158 + return 'Failed: Error while getting credentials';
159 + }
160 + }
161 +
162 + Future<String> ethSign(String topic, dynamic parameters) async {
163 + log('received eth sign request: $parameters');
164 +
165 + final String message;
166 + if (parameters[1] == null) {
167 + message = '';
168 + } else {
169 + message = parameters[1].toString().utf8Message;
170 + }
171 +
172 + final String? authError = await requestAuthorization(message);
173 + if (authError != null) {
174 + return authError;
175 + }
176 +
177 + try {
178 + // Load the private key
179 + final List<ChainKeyModel> keys = wcKeyService.getKeysForChain(getChainId());
180 +
181 + final EthPrivateKey credentials = EthPrivateKey.fromHex(keys[0].privateKey);
182 +
183 + final String signature = hex.encode(
184 + credentials.signPersonalMessageToUint8List(
185 + Uint8List.fromList(utf8.encode(message)),
186 + ),
187 + );
188 + log(signature);
189 +
190 + return '0x$signature';
191 + } catch (e) {
192 + log('error: ${e.toString()}');
193 + bottomSheetService.queueBottomSheet(
194 + isModalDismissible: true,
195 + widget: BottomSheetMessageDisplayWidget(message: '${S.current.error}: ${e.toString()}'),
196 + );
197 + return 'Failed';
198 + }
199 + }
200 +
201 + Future<String> ethSignTransaction(String topic, dynamic parameters) async {
202 + log('received eth sign transaction request: $parameters');
203 +
204 + final paramsData = parameters[0] as Map<String, dynamic>;
205 +
206 + final message = _convertToReadable(paramsData);
207 +
208 + final String? authError = await requestAuthorization(message);
209 +
210 + if (authError != null) {
211 + return authError;
212 + }
213 +
214 + // Load the private key
215 + final List<ChainKeyModel> keys = wcKeyService.getKeysForChain(getChainId());
216 +
217 + final Credentials credentials = EthPrivateKey.fromHex(keys[0].privateKey);
218 +
219 + WCEthereumTransactionModel ethTransaction =
220 + WCEthereumTransactionModel.fromJson(parameters[0] as Map<String, dynamic>);
221 +
222 + final transaction = Transaction(
223 + from: EthereumAddress.fromHex(ethTransaction.from),
224 + to: EthereumAddress.fromHex(ethTransaction.to),
225 + maxGas: ethTransaction.gasLimit != null ? int.tryParse(ethTransaction.gasLimit ?? "") : null,
226 + gasPrice: ethTransaction.gasPrice != null
227 + ? EtherAmount.inWei(BigInt.parse(ethTransaction.gasPrice ?? ""))
228 + : null,
229 + value: EtherAmount.inWei(BigInt.parse(ethTransaction.value)),
230 + data: hexToBytes(ethTransaction.data ?? ""),
231 + nonce: ethTransaction.nonce != null ? int.tryParse(ethTransaction.nonce ?? "") : null,
232 + );
233 +
234 + try {
235 + final result = await ethClient.sendTransaction(credentials, transaction);
236 +
237 + log('Result: $result');
238 +
239 + bottomSheetService.queueBottomSheet(
240 + isModalDismissible: true,
241 + widget: BottomSheetMessageDisplayWidget(
242 + message: S.current.awaitDAppProcessing,
243 + isError: false,
244 + ),
245 + );
246 +
247 + return result;
248 + } catch (e) {
249 + log('An error has occured while signing transaction: ${e.toString()}');
250 + bottomSheetService.queueBottomSheet(
251 + isModalDismissible: true,
252 + widget: BottomSheetMessageDisplayWidget(
253 + message: '${S.current.errorSigningTransaction}: ${e.toString()}',
254 + ),
255 + );
256 + return 'Failed';
257 + }
258 + }
259 +
260 + Future<String> ethSignTypedData(String topic, dynamic parameters) async {
261 + log('received eth sign typed data request: $parameters');
262 + final String? data = parameters[1] as String?;
263 +
264 + final String? authError = await requestAuthorization(data);
265 +
266 + if (authError != null) {
267 + return authError;
268 + }
269 +
270 + final List<ChainKeyModel> keys = wcKeyService.getKeysForChain(getChainId());
271 +
272 + return EthSigUtil.signTypedData(
273 + privateKey: keys[0].privateKey,
274 + jsonData: data ?? '',
275 + version: TypedDataVersion.V4,
276 + );
277 + }
278 +
279 + String _convertToReadable(Map<String, dynamic> data) {
280 + String gas = int.parse((data['gas'] as String).substring(2), radix: 16).toString();
281 + String value = data['value'] != null
282 + ? (int.parse((data['value'] as String).substring(2), radix: 16) / 1e18).toString() + ' ETH'
283 + : '0 ETH';
284 + String from = data['from'] as String;
285 + String to = data['to'] as String;
286 +
287 + return '''
288 + Gas: $gas\n
289 + Value: $value\n
290 + From: $from\n
291 + To: $to
292 + ''';
293 + }
294 +}
lib/core/wallet_connect/models/auth_request_model.dart new
+16
@@ -0,0 +1,16 @@
1 +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
2 +
3 +class AuthRequestModel {
4 + final String iss;
5 + final AuthRequest request;
6 +
7 + AuthRequestModel({
8 + required this.iss,
9 + required this.request,
10 + });
11 +
12 + @override
13 + String toString() {
14 + return 'AuthRequestModel(iss: $iss, request: $request)';
15 + }
16 +}
lib/core/wallet_connect/models/bottom_sheet_queue_item_model.dart new
+20
@@ -0,0 +1,20 @@
1 +import 'dart:async';
2 +
3 +import 'package:flutter/cupertino.dart';
4 +
5 +class BottomSheetQueueItemModel {
6 + final Widget widget;
7 + final bool isModalDismissible;
8 + final Completer<dynamic> completer;
9 +
10 + BottomSheetQueueItemModel({
11 + required this.widget,
12 + required this.completer,
13 + this.isModalDismissible = false,
14 + });
15 +
16 + @override
17 + String toString() {
18 + return 'BottomSheetQueueItemModel(widget: $widget, completer: $completer)';
19 + }
20 +}
lib/core/wallet_connect/models/chain_key_model.dart new
+16
@@ -0,0 +1,16 @@
1 +class ChainKeyModel {
2 + final List<String> chains;
3 + final String privateKey;
4 + final String publicKey;
5 +
6 + ChainKeyModel({
7 + required this.chains,
8 + required this.privateKey,
9 + required this.publicKey,
10 + });
11 +
12 + @override
13 + String toString() {
14 + return 'ChainKeyModel(chains: $chains, privateKey: $privateKey, publicKey: $publicKey)';
15 + }
16 +}
lib/core/wallet_connect/models/connection_model.dart new
+18
@@ -0,0 +1,18 @@
1 +class ConnectionModel {
2 + final String? title;
3 + final String? text;
4 + final List<String>? elements;
5 + final Map<String, void Function()>? elementActions;
6 +
7 + ConnectionModel({
8 + this.title,
9 + this.text,
10 + this.elements,
11 + this.elementActions,
12 + });
13 +
14 + @override
15 + String toString() {
16 + return 'WalletConnectRequestModel(title: $title, text: $text, elements: $elements, elementActions: $elementActions)';
17 + }
18 +}
lib/core/wallet_connect/models/session_request_model.dart new
+14
@@ -0,0 +1,14 @@
1 +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
2 +
3 +class SessionRequestModel {
4 + final ProposalData request;
5 +
6 + SessionRequestModel({
7 + required this.request,
8 + });
9 +
10 + @override
11 + String toString() {
12 + return 'SessionRequestModel(request: $request)';
13 + }
14 +}
lib/core/wallet_connect/wallet_connect_key_service.dart new
+72
@@ -0,0 +1,72 @@
1 +import 'package:cake_wallet/ethereum/ethereum.dart';
2 +import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart';
3 +import 'package:cw_core/balance.dart';
4 +import 'package:cw_core/transaction_history.dart';
5 +import 'package:cw_core/transaction_info.dart';
6 +import 'package:cw_core/wallet_base.dart';
7 +
8 +abstract class WalletConnectKeyService {
9 + /// Returns a list of all the keys.
10 + List<ChainKeyModel> getKeys();
11 +
12 + /// Returns a list of all the chain ids.
13 + List<String> getChains();
14 +
15 + /// Returns a list of all the keys for a given chain id.
16 + /// If the chain is not found, returns an empty list.
17 + /// - [chain]: The chain to get the keys for.
18 + List<ChainKeyModel> getKeysForChain(String chain);
19 +
20 + /// Returns a list of all the accounts in namespace:chainId:address format.
21 + List<String> getAllAccounts();
22 +}
23 +
24 +class KeyServiceImpl implements WalletConnectKeyService {
25 + KeyServiceImpl(this.wallet)
26 + : _keys = [
27 + ChainKeyModel(
28 + chains: [
29 + 'eip155:1',
30 + 'eip155:5',
31 + 'eip155:137',
32 + 'eip155:42161',
33 + 'eip155:80001',
34 + ],
35 + privateKey: ethereum!.getPrivateKey(wallet),
36 + publicKey: ethereum!.getPublicKey(wallet),
37 + ),
38 +
39 + ];
40 +
41 + late final WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
42 +
43 + late final List<ChainKeyModel> _keys;
44 +
45 + @override
46 + List<String> getChains() {
47 + final List<String> chainIds = [];
48 + for (final ChainKeyModel key in _keys) {
49 + chainIds.addAll(key.chains);
50 + }
51 + return chainIds;
52 + }
53 +
54 + @override
55 + List<ChainKeyModel> getKeys() => _keys;
56 +
57 + @override
58 + List<ChainKeyModel> getKeysForChain(String chain) {
59 + return _keys.where((e) => e.chains.contains(chain)).toList();
60 + }
61 +
62 + @override
63 + List<String> getAllAccounts() {
64 + final List<String> accounts = [];
65 + for (final ChainKeyModel key in _keys) {
66 + for (final String chain in key.chains) {
67 + accounts.add('$chain:${key.publicKey}');
68 + }
69 + }
70 + return accounts;
71 + }
72 +}
lib/core/wallet_connect/wc_bottom_sheet_service.dart new
+43
@@ -0,0 +1,43 @@
1 +import 'dart:async';
2 +import 'package:cake_wallet/core/wallet_connect/models/bottom_sheet_queue_item_model.dart';
3 +import 'package:flutter/material.dart';
4 +
5 +abstract class BottomSheetService {
6 + abstract final ValueNotifier<BottomSheetQueueItemModel?> currentSheet;
7 +
8 + Future<dynamic> queueBottomSheet({
9 + required Widget widget,
10 + bool isModalDismissible = false,
11 + });
12 +
13 + void resetCurrentSheet();
14 +}
15 +
16 +class BottomSheetServiceImpl implements BottomSheetService {
17 +
18 + @override
19 + final ValueNotifier<BottomSheetQueueItemModel?> currentSheet = ValueNotifier(null);
20 +
21 + @override
22 + Future<dynamic> queueBottomSheet({
23 + required Widget widget,
24 + bool isModalDismissible = false,
25 + }) async {
26 + // Create the bottom sheet queue item
27 + final completer = Completer<dynamic>();
28 + final queueItem = BottomSheetQueueItemModel(
29 + widget: widget,
30 + completer: completer,
31 + isModalDismissible: isModalDismissible,
32 + );
33 +
34 + currentSheet.value = queueItem;
35 +
36 + return await completer.future;
37 + }
38 +
39 + @override
40 + void resetCurrentSheet() {
41 + currentSheet.value = null;
42 + }
43 +}
lib/core/wallet_connect/web3wallet_service.dart new
+277
@@ -0,0 +1,277 @@
1 +import 'dart:async';
2 +import 'dart:developer';
3 +import 'dart:typed_data';
4 +
5 +import 'package:cake_wallet/core/wallet_connect/evm_chain_id.dart';
6 +import 'package:cake_wallet/core/wallet_connect/evm_chain_service.dart';
7 +import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
8 +import 'package:cake_wallet/generated/i18n.dart';
9 +import 'package:cake_wallet/core/wallet_connect/models/auth_request_model.dart';
10 +import 'package:cake_wallet/core/wallet_connect/models/chain_key_model.dart';
11 +import 'package:cake_wallet/core/wallet_connect/models/session_request_model.dart';
12 +import 'package:cake_wallet/src/screens/wallet_connect/widgets/connection_request_widget.dart';
13 +import 'package:cake_wallet/src/screens/wallet_connect/widgets/error_display_widget.dart';
14 +import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/web3_request_modal.dart';
15 +import 'package:cake_wallet/store/app_store.dart';
16 +import 'package:eth_sig_util/eth_sig_util.dart';
17 +import 'package:flutter/material.dart';
18 +import 'package:mobx/mobx.dart';
19 +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
20 +
21 +import 'wc_bottom_sheet_service.dart';
22 +import 'package:cake_wallet/.secrets.g.dart' as secrets;
23 +
24 +part 'web3wallet_service.g.dart';
25 +
26 +class Web3WalletService = Web3WalletServiceBase with _$Web3WalletService;
27 +
28 +abstract class Web3WalletServiceBase with Store {
29 + final AppStore appStore;
30 + final BottomSheetService _bottomSheetHandler;
31 + final WalletConnectKeyService walletKeyService;
32 +
33 + late Web3Wallet _web3Wallet;
34 +
35 + @observable
36 + bool isInitialized;
37 +
38 + /// The list of requests from the dapp
39 + /// Potential types include, but aren't limited to:
40 + /// [SessionProposalEvent], [AuthRequest]
41 + @observable
42 + ObservableList<PairingInfo> pairings;
43 +
44 + @observable
45 + ObservableList<SessionData> sessions;
46 +
47 + @observable
48 + ObservableList<StoredCacao> auth;
49 +
50 + Web3WalletServiceBase(this._bottomSheetHandler, this.walletKeyService, this.appStore)
51 + : pairings = ObservableList<PairingInfo>(),
52 + sessions = ObservableList<SessionData>(),
53 + auth = ObservableList<StoredCacao>(),
54 + isInitialized = false;
55 +
56 + @action
57 + void create() {
58 + // Create the web3wallet client
59 + _web3Wallet = Web3Wallet(
60 + core: Core(projectId: secrets.walletConnectProjectId),
61 + metadata: const PairingMetadata(
62 + name: 'Cake Wallet',
63 + description: 'Cake Wallet',
64 + url: 'https://cakewallet.com',
65 + icons: ['https://cakewallet.com/assets/image/cake_logo.png'],
66 + ),
67 + );
68 +
69 + // Setup our accounts
70 + List<ChainKeyModel> chainKeys = walletKeyService.getKeys();
71 + for (final chainKey in chainKeys) {
72 + for (final chainId in chainKey.chains) {
73 + _web3Wallet.registerAccount(
74 + chainId: chainId,
75 + accountAddress: chainKey.publicKey,
76 + );
77 + }
78 + }
79 +
80 + // Setup our listeners
81 + log('Created instance of web3wallet');
82 + _web3Wallet.core.pairing.onPairingInvalid.subscribe(_onPairingInvalid);
83 + _web3Wallet.core.pairing.onPairingCreate.subscribe(_onPairingCreate);
84 + _web3Wallet.core.pairing.onPairingDelete.subscribe(_onPairingDelete);
85 + _web3Wallet.core.pairing.onPairingExpire.subscribe(_onPairingDelete);
86 + _web3Wallet.pairings.onSync.subscribe(_onPairingsSync);
87 + _web3Wallet.onSessionProposal.subscribe(_onSessionProposal);
88 + _web3Wallet.onSessionProposalError.subscribe(_onSessionProposalError);
89 + _web3Wallet.onSessionConnect.subscribe(_onSessionConnect);
90 + _web3Wallet.onAuthRequest.subscribe(_onAuthRequest);
91 + }
92 +
93 + @action
94 + Future<void> init() async {
95 + // Await the initialization of the web3wallet
96 + log('Intializing web3wallet');
97 + if (!isInitialized) {
98 + try {
99 + await _web3Wallet.init();
100 + log('Initialized');
101 + isInitialized = true;
102 + } catch (e) {
103 + log('Experimentallllll: $e');
104 + isInitialized = false;
105 + }
106 + }
107 +
108 + _refreshPairings();
109 +
110 + final newSessions = _web3Wallet.sessions.getAll();
111 + sessions.addAll(newSessions);
112 +
113 + final newAuthRequests = _web3Wallet.completeRequests.getAll();
114 + auth.addAll(newAuthRequests);
115 +
116 + for (final cId in EVMChainId.values) {
117 + EvmChainServiceImpl(
118 + reference: cId,
119 + appStore: appStore,
120 + wcKeyService: walletKeyService,
121 + bottomSheetService: _bottomSheetHandler,
122 + wallet: _web3Wallet,
123 + );
124 + }
125 + }
126 +
127 + @action
128 + FutureOr<void> onDispose() {
129 + log('web3wallet dispose');
130 + _web3Wallet.core.pairing.onPairingInvalid.unsubscribe(_onPairingInvalid);
131 + _web3Wallet.pairings.onSync.unsubscribe(_onPairingsSync);
132 + _web3Wallet.onSessionProposal.unsubscribe(_onSessionProposal);
133 + _web3Wallet.onSessionProposalError.unsubscribe(_onSessionProposalError);
134 + _web3Wallet.onSessionConnect.unsubscribe(_onSessionConnect);
135 + _web3Wallet.onAuthRequest.unsubscribe(_onAuthRequest);
136 + _web3Wallet.core.pairing.onPairingDelete.unsubscribe(_onPairingDelete);
137 + _web3Wallet.core.pairing.onPairingExpire.unsubscribe(_onPairingDelete);
138 + }
139 +
140 + Web3Wallet getWeb3Wallet() {
141 + return _web3Wallet;
142 + }
143 +
144 + void _onPairingsSync(StoreSyncEvent? args) {
145 + if (args != null) {
146 + _refreshPairings();
147 + }
148 + }
149 +
150 + void _onPairingDelete(PairingEvent? event) {
151 + _refreshPairings();
152 + }
153 +
154 + @action
155 + void _refreshPairings() {
156 + pairings.clear();
157 + final allPairings = _web3Wallet.pairings.getAll();
158 + pairings.addAll(allPairings);
159 + }
160 +
161 + Future<void> _onSessionProposalError(SessionProposalErrorEvent? args) async {
162 + log(args.toString());
163 + }
164 +
165 + void _onSessionProposal(SessionProposalEvent? args) async {
166 + if (args != null) {
167 + final Widget modalWidget = Web3RequestModal(
168 + child: ConnectionRequestWidget(
169 + wallet: _web3Wallet,
170 + sessionProposal: SessionRequestModel(request: args.params),
171 + ),
172 + );
173 + // show the bottom sheet
174 + final bool? isApproved = await _bottomSheetHandler.queueBottomSheet(
175 + widget: modalWidget,
176 + ) as bool?;
177 +
178 + if (isApproved != null && isApproved) {
179 + _web3Wallet.approveSession(
180 + id: args.id,
181 + namespaces: args.params.generatedNamespaces!,
182 + );
183 + } else {
184 + _web3Wallet.rejectSession(
185 + id: args.id,
186 + reason: Errors.getSdkError(
187 + Errors.USER_REJECTED,
188 + ),
189 + );
190 + }
191 + }
192 + }
193 +
194 + @action
195 + void _onPairingInvalid(PairingInvalidEvent? args) {
196 + log('Pairing Invalid Event: $args');
197 + _bottomSheetHandler.queueBottomSheet(
198 + isModalDismissible: true,
199 + widget: BottomSheetMessageDisplayWidget(message: '${S.current.pairingInvalidEvent}: $args'),
200 + );
201 + }
202 +
203 + void _onPairingCreate(PairingEvent? args) {
204 + log('Pairing Create Event: $args');
205 + }
206 +
207 + @action
208 + void _onSessionConnect(SessionConnect? args) {
209 + if (args != null) {
210 + sessions.add(args.session);
211 + }
212 + }
213 +
214 + @action
215 + Future<void> _onAuthRequest(AuthRequest? args) async {
216 + if (args != null) {
217 + List<ChainKeyModel> chainKeys = walletKeyService.getKeysForChain('eip155:1');
218 + // Create the message to be signed
219 + final String iss = 'did:pkh:eip155:1:${chainKeys.first.publicKey}';
220 +
221 + final Widget modalWidget = Web3RequestModal(
222 + child: ConnectionRequestWidget(
223 + wallet: _web3Wallet,
224 + authRequest: AuthRequestModel(iss: iss, request: args),
225 + ),
226 + );
227 + final bool? isAuthenticated = await _bottomSheetHandler.queueBottomSheet(
228 + widget: modalWidget,
229 + ) as bool?;
230 +
231 + if (isAuthenticated != null && isAuthenticated) {
232 + final String message = _web3Wallet.formatAuthMessage(
233 + iss: iss,
234 + cacaoPayload: CacaoRequestPayload.fromPayloadParams(
235 + args.payloadParams,
236 + ),
237 + );
238 +
239 + final String sig = EthSigUtil.signPersonalMessage(
240 + message: Uint8List.fromList(message.codeUnits),
241 + privateKey: chainKeys.first.privateKey,
242 + );
243 +
244 + await _web3Wallet.respondAuthRequest(
245 + id: args.id,
246 + iss: iss,
247 + signature: CacaoSignature(
248 + t: CacaoSignature.EIP191,
249 + s: sig,
250 + ),
251 + );
252 + } else {
253 + await _web3Wallet.respondAuthRequest(
254 + id: args.id,
255 + iss: iss,
256 + error: Errors.getSdkError(
257 + Errors.USER_REJECTED_AUTH,
258 + ),
259 + );
260 + }
261 + }
262 + }
263 +
264 + @action
265 + Future<void> disconnectSession(String topic) async {
266 + final session = sessions.firstWhere((element) => element.pairingTopic == topic);
267 +
268 + await _web3Wallet.core.pairing.disconnect(topic: topic);
269 + await _web3Wallet.disconnectSession(
270 + topic: session.topic, reason: Errors.getSdkError(Errors.USER_DISCONNECTED));
271 + }
272 +
273 + @action
274 + List<SessionData> getSessionsForPairingInfo(PairingInfo pairing) {
275 + return sessions.where((element) => element.pairingTopic == pairing.topic).toList();
276 + }
277 +}
lib/di.dart
+34 -5
@@ -3,10 +3,12 @@ import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
3 import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
4 import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
5 import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
6 +import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
7 +import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
8 import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
9 +import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
10 import 'package:cake_wallet/core/yat_service.dart';
11 import 'package:cake_wallet/entities/background_tasks.dart';
9 -import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
12 import 'package:cake_wallet/entities/exchange_api_mode.dart';
13 import 'package:cake_wallet/entities/parse_address_from_domain.dart';
14 import 'package:cake_wallet/entities/receive_page_option.dart';
@@ -399,6 +401,10 @@ Future<void> setup({
401 }
402 if (appStore.wallet != null) {
403 authStore.allowed();
404 +
405 + if (appStore.wallet!.type == WalletType.ethereum) {
406 + getIt.get<Web3WalletService>().init();
407 + }
408 return;
409 }
410
@@ -419,6 +425,10 @@ Future<void> setup({
425 } else {
426 if (appStore.wallet != null) {
427 authStore.allowed();
428 +
429 + if (appStore.wallet!.type == WalletType.ethereum) {
430 + getIt.get<Web3WalletService>().init();
431 + }
432 return;
433 }
434
@@ -438,11 +448,28 @@ Future<void> setup({
448 }, closable: false);
449 }, instanceName: 'login');
450
451 + getIt.registerSingleton<BottomSheetService>(BottomSheetServiceImpl());
452 +
453 + final appStore = getIt.get<AppStore>();
454 +
455 + getIt.registerLazySingleton<WalletConnectKeyService>(() => KeyServiceImpl(appStore.wallet!));
456 +
457 + getIt.registerLazySingleton<Web3WalletService>(() {
458 + final Web3WalletService web3WalletService = Web3WalletService(
459 + getIt.get<BottomSheetService>(),
460 + getIt.get<WalletConnectKeyService>(),
461 + appStore,
462 + );
463 + web3WalletService.create();
464 + return web3WalletService;
465 + });
466 +
467 getIt.registerFactory(() => BalancePage(
468 dashboardViewModel: getIt.get<DashboardViewModel>(),
469 settingsStore: getIt.get<SettingsStore>()));
470
471 getIt.registerFactory<DashboardPage>(() => DashboardPage(
472 + bottomSheetService: getIt.get<BottomSheetService>(),
473 balancePage: getIt.get<BalancePage>(),
474 dashboardViewModel: getIt.get<DashboardViewModel>(),
475 addressListViewModel: getIt.get<WalletAddressListViewModel>(),
@@ -459,6 +486,7 @@ Future<void> setup({
486 });
487 getIt.registerFactoryParam<DesktopDashboardPage, GlobalKey<NavigatorState>, void>(
488 (desktopKey, _) => DesktopDashboardPage(
489 + bottomSheetService: getIt.get<BottomSheetService>(),
490 balancePage: getIt.get<BalancePage>(),
491 dashboardViewModel: getIt.get<DashboardViewModel>(),
492 addressListViewModel: getIt.get<WalletAddressListViewModel>(),
@@ -668,7 +696,9 @@ Future<void> setup({
696 return NodeListViewModel(_nodeSource, appStore);
697 });
698
671 - getIt.registerFactory(() => ConnectionSyncPage(getIt.get<DashboardViewModel>()));
699 + getIt.registerFactory(
700 + () => ConnectionSyncPage(getIt.get<DashboardViewModel>(), getIt.get<Web3WalletService>()),
701 + );
702
703 getIt.registerFactory(
704 () => SecurityBackupPage(getIt.get<SecuritySettingsViewModel>(), getIt.get<AuthService>()));
@@ -851,9 +881,8 @@ Future<void> setup({
881
882 getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>()));
883
854 - getIt.registerFactory(() =>
855 - SupportChatPage(
856 - getIt.get<SupportViewModel>(), secureStorage: getIt.get<FlutterSecureStorage>()));
884 + getIt.registerFactory(() => SupportChatPage(getIt.get<SupportViewModel>(),
885 + secureStorage: getIt.get<FlutterSecureStorage>()));
886
887 getIt.registerFactory(() => SupportOtherLinksPage(getIt.get<SupportViewModel>()));
888
lib/entities/preferences_key.dart
+2 -4
@@ -15,8 +15,7 @@ class PreferencesKey {
15 static const disableSellKey = 'disable_sell';
16 static const defaultBuyProvider = 'default_buy_provider';
17 static const currentFiatApiModeKey = 'current_fiat_api_mode';
18 - static const allowBiometricalAuthenticationKey =
19 - 'allow_biometrical_authentication';
18 + static const allowBiometricalAuthenticationKey = 'allow_biometrical_authentication';
19 static const useTOTP2FA = 'use_totp_2fa';
20 static const failedTotpTokenTrials = 'failed_token_trials';
21 static const disableExchangeKey = 'disable_exchange';
@@ -54,8 +53,7 @@ class PreferencesKey {
53 static const clearnetDonationLink = 'clearnet_donation_link';
54 static const onionDonationLink = 'onion_donation_link';
55 static const lastSeenAppVersion = 'last_seen_app_version';
57 - static const shouldShowMarketPlaceInDashboard =
58 - 'should_show_marketplace_in_dashboard';
56 + static const shouldShowMarketPlaceInDashboard = 'should_show_marketplace_in_dashboard';
57 static const isNewInstall = 'is_new_install';
58 static const shouldRequireTOTP2FAForAccessingWallet =
59 'should_require_totp_2fa_for_accessing_wallets';
lib/ethereum/cw_ethereum.dart
+14
@@ -33,6 +33,20 @@ class CWEthereum extends Ethereum {
33 @override
34 String getAddress(WalletBase wallet) => (wallet as EthereumWallet).walletAddresses.address;
35
36 + @override
37 + String getPrivateKey(WalletBase wallet) {
38 + final privateKeyHolder = (wallet as EthereumWallet).ethPrivateKey;
39 + String stringKey = bytesToHex(privateKeyHolder.privateKey);
40 + return stringKey;
41 + }
42 +
43 + @override
44 + String getPublicKey(WalletBase wallet) {
45 + final privateKeyInUnitInt = (wallet as EthereumWallet).ethPrivateKey;
46 + final publicKey = privateKeyInUnitInt.address.hex;
47 + return publicKey;
48 + }
49 +
50 @override
51 TransactionPriority getDefaultTransactionPriority() => EthereumTransactionPriority.medium;
52
lib/main.dart
+10 -11
@@ -39,7 +39,6 @@ import 'package:cake_wallet/src/screens/root/root.dart';
39 import 'package:uni_links/uni_links.dart';
40 import 'package:cw_core/unspent_coins_info.dart';
41 import 'package:cake_wallet/monero/monero.dart';
42 -import 'package:cake_wallet/wallet_type_utils.dart';
42 import 'package:cw_core/cake_hive.dart';
43
44 final navigatorKey = GlobalKey<NavigatorState>();
@@ -155,7 +154,7 @@ Future<void> initializeAppConfigs() async {
154 secureStorage: secureStorage,
155 anonpayInvoiceInfo: anonpayInvoiceInfo,
156 initialMigrationVersion: 21);
158 - }
157 +}
158
159 Future<void> initialSetup(
160 {required SharedPreferences sharedPreferences,
@@ -308,26 +307,26 @@ class _Home extends StatefulWidget {
307 }
308
309 class _HomeState extends State<_Home> {
311 - @override
310 + @override
311 void didChangeDependencies() {
313 - if(!ResponsiveLayoutUtil.instance.isMobile){
314 - _setOrientation(context);
312 + if (!ResponsiveLayoutUtil.instance.isMobile) {
313 + _setOrientation(context);
314 }
315 super.didChangeDependencies();
316 }
317
319 -
320 - void _setOrientation(BuildContext context){
318 + void _setOrientation(BuildContext context) {
319 final orientation = MediaQuery.of(context).orientation;
320 final width = MediaQuery.of(context).size.width;
321 final height = MediaQuery.of(context).size.height;
322 if (orientation == Orientation.portrait && width < height) {
325 - SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
323 + SystemChrome.setPreferredOrientations(
324 + [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
325 } else if (orientation == Orientation.landscape && width > height) {
327 - SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
326 + SystemChrome.setPreferredOrientations(
327 + [DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
328 }
329 -
330 - }
329 + }
330
331 @override
332 Widget build(BuildContext context) {
lib/src/screens/dashboard/dashboard_page.dart
+96 -83
@@ -1,9 +1,12 @@
1 import 'dart:async';
2 +import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
3 +import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
4 import 'package:cake_wallet/entities/preferences_key.dart';
5 import 'package:cake_wallet/di.dart';
6 import 'package:cake_wallet/entities/main_actions.dart';
7 import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
8 import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart';
9 +import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart';
10 import 'package:cake_wallet/src/widgets/gradient_background.dart';
11 import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
12 import 'package:cake_wallet/utils/device_info.dart';
@@ -35,12 +38,14 @@ import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
38
39 class DashboardPage extends StatelessWidget {
40 DashboardPage({
41 + required this.bottomSheetService,
42 required this.balancePage,
43 required this.dashboardViewModel,
44 required this.addressListViewModel,
45 });
46
47 final BalancePage balancePage;
48 + final BottomSheetService bottomSheetService;
49 final DashboardViewModel dashboardViewModel;
50 final WalletAddressListViewModel addressListViewModel;
51
@@ -55,12 +60,14 @@ class DashboardPage extends StatelessWidget {
60 } else {
61 return _DashboardPageView(
62 balancePage: balancePage,
63 + bottomSheetService: bottomSheetService,
64 dashboardViewModel: dashboardViewModel,
65 addressListViewModel: addressListViewModel,
66 );
67 }
68 } else if (ResponsiveLayoutUtil.instance.shouldRenderMobileUI()) {
69 return _DashboardPageView(
70 + bottomSheetService: bottomSheetService,
71 balancePage: balancePage,
72 dashboardViewModel: dashboardViewModel,
73 addressListViewModel: addressListViewModel,
@@ -76,6 +83,7 @@ class DashboardPage extends StatelessWidget {
83
84 class _DashboardPageView extends BasePage {
85 _DashboardPageView({
86 + required this.bottomSheetService,
87 required this.balancePage,
88 required this.dashboardViewModel,
89 required this.addressListViewModel,
@@ -126,6 +134,7 @@ class _DashboardPageView extends BasePage {
134 }
135
136 final DashboardViewModel dashboardViewModel;
137 + final BottomSheetService bottomSheetService;
138 final WalletAddressListViewModel addressListViewModel;
139
140 int get initialPage => dashboardViewModel.shouldShowMarketPlaceInDashboard ? 1 : 0;
@@ -158,102 +167,106 @@ class _DashboardPageView extends BasePage {
167
168 return SafeArea(
169 minimum: EdgeInsets.only(bottom: 24),
161 - child: Column(
162 - mainAxisSize: MainAxisSize.max,
163 - children: <Widget>[
164 - Expanded(
165 - child: Observer(
166 - builder: (context) {
167 - return PageView.builder(
168 - controller: controller,
169 - itemCount: pages.length,
170 - itemBuilder: (context, index) => pages[index],
171 - );
172 - },
173 - ),
174 - ),
175 - Padding(
176 - padding: EdgeInsets.only(bottom: 24, top: 10),
177 - child: Observer(
178 - builder: (context) {
179 - return ExcludeSemantics(
180 - child: SmoothPageIndicator(
170 + child: BottomSheetListener(
171 + bottomSheetService: bottomSheetService,
172 + child: Column(
173 + mainAxisSize: MainAxisSize.max,
174 + children: <Widget>[
175 + Expanded(
176 + child: Observer(
177 + builder: (context) {
178 + return PageView.builder(
179 controller: controller,
182 - count: pages.length,
183 - effect: ColorTransitionEffect(
184 - spacing: 6.0,
185 - radius: 6.0,
186 - dotWidth: 6.0,
187 - dotHeight: 6.0,
188 - dotColor: Theme.of(context).indicatorColor,
189 - activeDotColor: Theme.of(context)
190 - .extension<DashboardPageTheme>()!
191 - .indicatorDotTheme
192 - .activeIndicatorColor,
193 - ),
194 - ),
195 - );
196 - },
180 + itemCount: pages.length,
181 + itemBuilder: (context, index) => pages[index],
182 + );
183 + },
184 + ),
185 ),
198 - ),
199 - Observer(
200 - builder: (_) {
201 - return ClipRect(
202 - child: Container(
203 - margin: const EdgeInsets.only(left: 16, right: 16),
204 - child: Container(
205 - decoration: BoxDecoration(
206 - borderRadius: BorderRadius.circular(50.0),
207 - border: Border.all(
208 - color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
209 - width: 1,
186 + Padding(
187 + padding: EdgeInsets.only(bottom: 24, top: 10),
188 + child: Observer(
189 + builder: (context) {
190 + return ExcludeSemantics(
191 + child: SmoothPageIndicator(
192 + controller: controller,
193 + count: pages.length,
194 + effect: ColorTransitionEffect(
195 + spacing: 6.0,
196 + radius: 6.0,
197 + dotWidth: 6.0,
198 + dotHeight: 6.0,
199 + dotColor: Theme.of(context).indicatorColor,
200 + activeDotColor: Theme.of(context)
201 + .extension<DashboardPageTheme>()!
202 + .indicatorDotTheme
203 + .activeIndicatorColor,
204 ),
211 - color:
212 - Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
205 ),
206 + );
207 + },
208 + ),
209 + ),
210 + Observer(
211 + builder: (_) {
212 + return ClipRect(
213 + child: Container(
214 + margin: const EdgeInsets.only(left: 16, right: 16),
215 child: Container(
215 - padding: EdgeInsets.only(left: 32, right: 32),
216 - child: Row(
217 - mainAxisAlignment: MainAxisAlignment.spaceBetween,
218 - children: MainActions.all
219 - .where((element) => element.canShow?.call(dashboardViewModel) ?? true)
220 - .map(
221 - (action) => Semantics(
222 - button: true,
223 - enabled: (action.isEnabled?.call(dashboardViewModel) ?? true),
224 - child: ActionButton(
225 - image: Image.asset(
226 - action.image,
227 - height: 24,
228 - width: 24,
229 - color: action.isEnabled?.call(dashboardViewModel) ?? true
230 - ? Theme.of(context)
231 - .extension<DashboardPageTheme>()!
232 - .mainActionsIconColor
216 + decoration: BoxDecoration(
217 + borderRadius: BorderRadius.circular(50.0),
218 + border: Border.all(
219 + color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
220 + width: 1,
221 + ),
222 + color: Theme.of(context)
223 + .extension<SyncIndicatorTheme>()!
224 + .syncedBackgroundColor,
225 + ),
226 + child: Container(
227 + padding: EdgeInsets.only(left: 32, right: 32),
228 + child: Row(
229 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
230 + children: MainActions.all
231 + .where((element) => element.canShow?.call(dashboardViewModel) ?? true)
232 + .map(
233 + (action) => Semantics(
234 + button: true,
235 + enabled: (action.isEnabled?.call(dashboardViewModel) ?? true),
236 + child: ActionButton(
237 + image: Image.asset(
238 + action.image,
239 + height: 24,
240 + width: 24,
241 + color: action.isEnabled?.call(dashboardViewModel) ?? true
242 + ? Theme.of(context)
243 + .extension<DashboardPageTheme>()!
244 + .mainActionsIconColor
245 + : Theme.of(context)
246 + .extension<BalancePageTheme>()!
247 + .labelTextColor,
248 + ),
249 + title: action.name(context),
250 + onClick: () async =>
251 + await action.onTap(context, dashboardViewModel),
252 + textColor: action.isEnabled?.call(dashboardViewModel) ?? true
253 + ? null
254 : Theme.of(context)
255 .extension<BalancePageTheme>()!
256 .labelTextColor,
257 ),
237 - title: action.name(context),
238 - onClick: () async =>
239 - await action.onTap(context, dashboardViewModel),
240 - textColor: action.isEnabled?.call(dashboardViewModel) ?? true
241 - ? null
242 - : Theme.of(context)
243 - .extension<BalancePageTheme>()!
244 - .labelTextColor,
258 ),
246 - ),
247 - )
248 - .toList(),
259 + )
260 + .toList(),
261 + ),
262 ),
263 ),
264 ),
252 - ),
253 - );
254 - },
255 - ),
256 - ],
265 + );
266 + },
267 + ),
268 + ],
269 + ),
270 ),
271 );
272 }
lib/src/screens/dashboard/desktop_dashboard_page.dart
+30 -23
@@ -1,8 +1,10 @@
1 import 'dart:async';
2 +import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
3 import 'package:cake_wallet/entities/preferences_key.dart';
4 import 'package:cake_wallet/generated/i18n.dart';
5 import 'package:cake_wallet/routes.dart';
6 import 'package:cake_wallet/src/screens/release_notes/release_notes_screen.dart';
7 +import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart';
8 import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
9 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
10 import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -19,12 +21,14 @@ import 'package:shared_preferences/shared_preferences.dart';
21 class DesktopDashboardPage extends StatelessWidget {
22 DesktopDashboardPage({
23 required this.balancePage,
24 + required this.bottomSheetService,
25 required this.dashboardViewModel,
26 required this.addressListViewModel,
27 required this.desktopKey,
28 });
29
30 final BalancePage balancePage;
31 + final BottomSheetService bottomSheetService;
32 final DashboardViewModel dashboardViewModel;
33 final WalletAddressListViewModel addressListViewModel;
34 final GlobalKey<NavigatorState> desktopKey;
@@ -36,31 +40,34 @@ class DesktopDashboardPage extends StatelessWidget {
40 Widget build(BuildContext context) {
41 _setEffects(context);
42
39 - return Container(
40 - color: Theme.of(context).colorScheme.background,
41 - child: Row(
42 - crossAxisAlignment: CrossAxisAlignment.start,
43 - children: [
44 - Container(
45 - width: 400,
46 - child: balancePage,
47 - ),
48 - Flexible(
49 - child: ConstrainedBox(
50 - constraints: BoxConstraints(maxWidth: 500),
51 - child: Navigator(
52 - key: desktopKey,
53 - initialRoute: Routes.desktop_actions,
54 - onGenerateRoute: (settings) => Router.createRoute(settings),
55 - onGenerateInitialRoutes: (NavigatorState navigator, String initialRouteName) {
56 - return [
57 - navigator.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))!
58 - ];
59 - },
43 + return BottomSheetListener(
44 + bottomSheetService: bottomSheetService,
45 + child: Container(
46 + color: Theme.of(context).colorScheme.background,
47 + child: Row(
48 + crossAxisAlignment: CrossAxisAlignment.start,
49 + children: [
50 + Container(
51 + width: 400,
52 + child: balancePage,
53 + ),
54 + Flexible(
55 + child: ConstrainedBox(
56 + constraints: BoxConstraints(maxWidth: 500),
57 + child: Navigator(
58 + key: desktopKey,
59 + initialRoute: Routes.desktop_actions,
60 + onGenerateRoute: (settings) => Router.createRoute(settings),
61 + onGenerateInitialRoutes: (NavigatorState navigator, String initialRouteName) {
62 + return [
63 + navigator.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))!
64 + ];
65 + },
66 + ),
67 ),
68 ),
62 - ),
63 - ],
69 + ],
70 + ),
71 ),
72 );
73 }
lib/src/screens/root/root.dart
+9 -7
@@ -1,6 +1,7 @@
1 import 'dart:async';
2 import 'package:cake_wallet/core/auth_service.dart';
3 import 'package:cake_wallet/core/totp_request_details.dart';
4 +import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
5 import 'package:cake_wallet/utils/device_info.dart';
6 import 'package:cake_wallet/utils/payment_request.dart';
7 import 'package:flutter/material.dart';
@@ -97,8 +98,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
98 return;
99 }
100
100 - if (!_isInactive &&
101 - widget.authenticationStore.state == AuthenticationState.allowed) {
101 + if (!_isInactive && widget.authenticationStore.state == AuthenticationState.allowed) {
102 setState(() => _setInactive(true));
103 }
104
@@ -125,16 +125,15 @@ class RootState extends State<Root> with WidgetsBindingObserver {
125 return;
126 } else {
127 final useTotp = widget.appStore.settingsStore.useTOTP2FA;
128 - final shouldUseTotp2FAToAccessWallets = widget.appStore
129 - .settingsStore.shouldRequireTOTP2FAForAccessingWallet;
128 + final shouldUseTotp2FAToAccessWallets =
129 + widget.appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
130 if (useTotp && shouldUseTotp2FAToAccessWallets) {
131 _reset();
132 auth.close(
133 route: Routes.totpAuthCodePage,
134 arguments: TotpAuthArgumentsModel(
135 onTotpAuthenticationFinished:
136 - (bool isAuthenticatedSuccessfully,
137 - TotpAuthCodePageState totpAuth) {
136 + (bool isAuthenticatedSuccessfully, TotpAuthCodePageState totpAuth) {
137 if (!isAuthenticatedSuccessfully) {
138 return;
139 }
@@ -169,7 +168,10 @@ class RootState extends State<Root> with WidgetsBindingObserver {
168 launchUri = null;
169 }
170
172 - return WillPopScope(onWillPop: () async => false, child: widget.child);
171 + return WillPopScope(
172 + onWillPop: () async => false,
173 + child: widget.child,
174 + );
175 }
176
177 void _reset() {
lib/src/screens/settings/connection_sync_page.dart
+20 -2
@@ -1,12 +1,15 @@
1 +import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
2 import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
3 import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
4 import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
5 +import 'package:cake_wallet/src/screens/settings/widgets/wallet_connect_button.dart';
6 +import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
7 import 'package:cake_wallet/utils/device_info.dart';
8 import 'package:cake_wallet/utils/show_pop_up.dart';
9 import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
10 import 'package:cake_wallet/view_model/settings/sync_mode.dart';
11 +import 'package:cw_core/wallet_type.dart';
12 import 'package:flutter/material.dart';
9 -import 'package:flutter/cupertino.dart';
13 import 'package:cake_wallet/routes.dart';
14 import 'package:cake_wallet/generated/i18n.dart';
15 import 'package:cake_wallet/src/screens/base_page.dart';
@@ -15,11 +18,12 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
18 import 'package:flutter_mobx/flutter_mobx.dart';
19
20 class ConnectionSyncPage extends BasePage {
18 - ConnectionSyncPage(this.dashboardViewModel);
21 + ConnectionSyncPage(this.dashboardViewModel, this.web3walletService);
22
23 @override
24 String get title => S.current.connection_sync;
25
26 + final Web3WalletService web3walletService;
27 final DashboardViewModel dashboardViewModel;
28
29 @override
@@ -66,6 +70,20 @@ class ConnectionSyncPage extends BasePage {
70 handler: (context) => Navigator.of(context).pushNamed(Routes.manageNodes),
71 ),
72 const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
73 + if (dashboardViewModel.wallet.type == WalletType.ethereum) ...[
74 + WalletConnectTile(
75 + onTap: () async {
76 + Navigator.of(context).push(
77 + MaterialPageRoute(
78 + builder: (context) {
79 + return WalletConnectConnectionsView(web3walletService: web3walletService);
80 + },
81 + ),
82 + );
83 + },
84 + ),
85 + const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
86 + ]
87 ],
88 ),
89 );
lib/src/screens/settings/widgets/wallet_connect_button.dart new
+46
@@ -0,0 +1,46 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
3 +import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
4 +import 'package:flutter/material.dart';
5 +
6 +class WalletConnectTile extends StatelessWidget {
7 + const WalletConnectTile({required this.onTap});
8 +
9 + final VoidCallback onTap;
10 +
11 + @override
12 + Widget build(BuildContext context) {
13 + return GestureDetector(
14 + onTap: onTap,
15 + child: Padding(
16 + padding: EdgeInsets.all(24),
17 + child: Row(
18 + mainAxisAlignment: MainAxisAlignment.start,
19 + crossAxisAlignment: CrossAxisAlignment.center,
20 + children: <Widget>[
21 + Image.asset(
22 + 'assets/images/walletconnect_logo.png',
23 + height: 24,
24 + width: 24,
25 + ),
26 + SizedBox(width: 16),
27 + Expanded(
28 + child: Text(
29 + S.current.walletConnect,
30 + style: TextStyle(
31 + fontSize: 14,
32 + fontWeight: FontWeight.normal,
33 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
34 + ),
35 + ),
36 + ),
37 + Image.asset(
38 + 'assets/images/select_arrow.png',
39 + color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
40 + )
41 + ],
42 + ),
43 + ),
44 + );
45 + }
46 +}
lib/src/screens/wallet_connect/utils/namespace_model_builder.dart new
+71
@@ -0,0 +1,71 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:cake_wallet/src/screens/wallet_connect/widgets/connection_widget.dart';
3 +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
4 +
5 +import '../../../../core/wallet_connect/models/connection_model.dart';
6 +
7 +class ConnectionWidgetBuilder {
8 + static List<ConnectionWidget> buildFromRequiredNamespaces(
9 + Map<String, RequiredNamespace> requiredNamespaces,
10 + ) {
11 + final List<ConnectionWidget> views = [];
12 + for (final key in requiredNamespaces.keys) {
13 + RequiredNamespace ns = requiredNamespaces[key]!;
14 + final List<ConnectionModel> models = [];
15 + // If the chains property is present, add the chain data to the models
16 + if (ns.chains != null) {
17 + models.add(ConnectionModel(title: S.current.chains, elements: ns.chains!));
18 + }
19 + models.add(ConnectionModel(title: S.current.methods, elements: ns.methods));
20 + models.add(ConnectionModel(title: S.current.events, elements: ns.events));
21 +
22 + views.add(ConnectionWidget(title: key, info: models));
23 + }
24 +
25 + return views;
26 + }
27 +
28 + static List<ConnectionWidget> buildFromNamespaces(
29 + String topic,
30 + Map<String, Namespace> namespaces,
31 + Web3Wallet web3wallet,
32 + ) {
33 + final List<ConnectionWidget> views = [];
34 + for (final key in namespaces.keys) {
35 + final Namespace ns = namespaces[key]!;
36 + final List<ConnectionModel> models = [];
37 + // If the chains property is present, add the chain data to the models
38 + models.add(
39 + ConnectionModel(
40 + title: S.current.chains,
41 + elements: ns.accounts,
42 + ),
43 + );
44 + models.add(ConnectionModel(
45 + title: S.current.methods,
46 + elements: ns.methods,
47 + ));
48 +
49 + Map<String, void Function()> actions = {};
50 + for (final String event in ns.events) {
51 + actions[event] = () async {
52 + final String chainId = NamespaceUtils.isValidChainId(key)
53 + ? key
54 + : NamespaceUtils.getChainFromAccount(ns.accounts.first);
55 + await web3wallet.emitSessionEvent(
56 + topic: topic,
57 + chainId: chainId,
58 + event: SessionEventParams(name: event, data: '${S.current.event}: $event'),
59 + );
60 + };
61 + }
62 + models.add(
63 + ConnectionModel(title: S.current.events, elements: ns.events, elementActions: actions),
64 + );
65 +
66 + views.add(ConnectionWidget(title: key, info: models));
67 + }
68 +
69 + return views;
70 + }
71 +}
lib/src/screens/wallet_connect/utils/string_parsing.dart new
+16
@@ -0,0 +1,16 @@
1 +import 'dart:convert';
2 +
3 +import 'package:convert/convert.dart';
4 +
5 +extension StringParsing on String {
6 + String get utf8Message {
7 + if (startsWith('0x')) {
8 + final List<int> decoded = hex.decode(
9 + substring(2),
10 + );
11 + return utf8.decode(decoded);
12 + }
13 +
14 + return this;
15 + }
16 +}
lib/src/screens/wallet_connect/wc_connections_listing_view.dart new
+142
@@ -0,0 +1,142 @@
1 +import 'dart:developer';
2 +import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
3 +import 'package:cake_wallet/generated/i18n.dart';
4 +import 'package:cake_wallet/src/screens/base_page.dart';
5 +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
6 +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
7 +import 'package:flutter/material.dart';
8 +import 'package:flutter_mobx/flutter_mobx.dart';
9 +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
10 +import 'package:cake_wallet/entities/qr_scanner.dart';
11 +import 'package:cake_wallet/src/widgets/primary_button.dart';
12 +import 'package:cake_wallet/utils/show_pop_up.dart';
13 +
14 +import 'widgets/pairing_item_widget.dart';
15 +import 'wc_pairing_detail_page.dart';
16 +
17 +class WalletConnectConnectionsView extends StatelessWidget {
18 + final Web3WalletService web3walletService;
19 +
20 + WalletConnectConnectionsView({required this.web3walletService, Key? key}) : super(key: key);
21 +
22 + @override
23 + Widget build(BuildContext context) {
24 + return WCPairingsWidget(web3walletService: web3walletService);
25 + }
26 +}
27 +
28 +class WCPairingsWidget extends BasePage {
29 + WCPairingsWidget({required this.web3walletService, Key? key})
30 + : web3wallet = web3walletService.getWeb3Wallet();
31 +
32 + final Web3Wallet web3wallet;
33 + final Web3WalletService web3walletService;
34 +
35 + @override
36 + String get title => S.current.walletConnect;
37 +
38 + Future<void> _onScanQrCode(BuildContext context, Web3Wallet web3Wallet) async {
39 + final String? uri = await presentQRScanner();
40 +
41 + if (uri == null) return _invalidUriToast(context, S.current.nullURIError);
42 +
43 + try {
44 + log('_onFoundUri: $uri');
45 + final Uri uriData = Uri.parse(uri);
46 + await web3Wallet.pair(uri: uriData);
47 + } on WalletConnectError catch (e) {
48 + await _invalidUriToast(context, e.message);
49 + } catch (e) {
50 + await _invalidUriToast(context, e.toString());
51 + }
52 + }
53 +
54 + Future<void> _invalidUriToast(BuildContext context, String message) async {
55 + await showPopUp<void>(
56 + context: context,
57 + builder: (BuildContext context) {
58 + return AlertWithOneAction(
59 + alertTitle: S.of(context).error,
60 + alertContent: message,
61 + buttonText: S.of(context).ok,
62 + buttonAction: Navigator.of(context).pop,
63 + alertBarrierDismissible: false,
64 + );
65 + },
66 + );
67 + }
68 +
69 + @override
70 + Widget body(BuildContext context) {
71 + return Observer(
72 + builder: (context) {
73 + return Column(
74 + children: [
75 + Padding(
76 + padding: EdgeInsets.symmetric(horizontal: 24),
77 + child: Column(
78 + children: [
79 + SizedBox(height: 24),
80 + Text(
81 + S.current.connectWalletPrompt,
82 + style: TextStyle(
83 + fontSize: 16.0,
84 + fontWeight: FontWeight.normal,
85 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
86 + ),
87 + ),
88 + SizedBox(height: 16),
89 + PrimaryButton(
90 + text: S.current.newConnection,
91 + color: Theme.of(context).primaryColor,
92 + textColor: Colors.white,
93 + onPressed: () => _onScanQrCode(context, web3wallet),
94 + ),
95 + ],
96 + ),
97 + ),
98 + SizedBox(height: 48),
99 + Expanded(
100 + child: Visibility(
101 + visible: web3walletService.pairings.isEmpty,
102 + child: Center(
103 + child: Text(
104 + S.current.activeConnectionsPrompt,
105 + textAlign: TextAlign.center,
106 + style: TextStyle(
107 + fontSize: 16.0,
108 + fontWeight: FontWeight.normal,
109 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
110 + ),
111 + ),
112 + ),
113 + replacement: ListView.builder(
114 + itemCount: web3walletService.pairings.length,
115 + itemBuilder: (BuildContext context, int index) {
116 + final pairing = web3walletService.pairings[index];
117 + return PairingItemWidget(
118 + key: ValueKey(pairing.topic),
119 + pairing: pairing,
120 + onTap: () {
121 + Navigator.push(
122 + context,
123 + MaterialPageRoute(
124 + builder: (context) => WalletConnectPairingDetailsPage(
125 + pairing: pairing,
126 + web3walletService: web3walletService,
127 + ),
128 + ),
129 + );
130 + },
131 + );
132 + },
133 + ),
134 + ),
135 + ),
136 + SizedBox(height: 48),
137 + ],
138 + );
139 + },
140 + );
141 + }
142 +}
lib/src/screens/wallet_connect/wc_pairing_detail_page.dart new
+186
@@ -0,0 +1,186 @@
1 +import 'dart:developer';
2 +
3 +import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
4 +import 'package:cake_wallet/generated/i18n.dart';
5 +import 'package:cake_wallet/src/screens/base_page.dart';
6 +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
7 +import 'package:cake_wallet/src/widgets/primary_button.dart';
8 +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
9 +import 'package:cake_wallet/utils/show_pop_up.dart';
10 +import 'package:flutter/material.dart';
11 +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
12 +
13 +import 'utils/namespace_model_builder.dart';
14 +
15 +class WalletConnectPairingDetailsPage extends StatefulWidget {
16 + final PairingInfo pairing;
17 + final Web3WalletService web3walletService;
18 +
19 + const WalletConnectPairingDetailsPage({
20 + required this.pairing,
21 + required this.web3walletService,
22 + super.key,
23 + });
24 +
25 + @override
26 + WalletConnectPairingDetailsPageState createState() => WalletConnectPairingDetailsPageState();
27 +}
28 +
29 +class WalletConnectPairingDetailsPageState extends State<WalletConnectPairingDetailsPage> {
30 + List<Widget> sessionWidgets = [];
31 + late String expiryDate;
32 + @override
33 + void initState() {
34 + super.initState();
35 + initDateTime();
36 + initSessions();
37 + }
38 +
39 + void initDateTime() {
40 + DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(widget.pairing.expiry * 1000);
41 + int year = dateTime.year;
42 + int month = dateTime.month;
43 + int day = dateTime.day;
44 +
45 + expiryDate = '$year-${month.toString().padLeft(2, '0')}-${day.toString().padLeft(2, '0')}';
46 + }
47 +
48 + void initSessions() {
49 + List<SessionData> sessions = widget.web3walletService.getSessionsForPairingInfo(widget.pairing);
50 +
51 + for (final SessionData session in sessions) {
52 + List<Widget> namespaceWidget = ConnectionWidgetBuilder.buildFromNamespaces(
53 + session.topic,
54 + session.namespaces,
55 + widget.web3walletService.getWeb3Wallet(),
56 + );
57 + // Loop through and add the namespace widgets, but put 20 pixels between each one
58 + for (int i = 0; i < namespaceWidget.length; i++) {
59 + sessionWidgets.add(namespaceWidget[i]);
60 + if (i != namespaceWidget.length - 1) {
61 + sessionWidgets.add(const SizedBox(height: 20.0));
62 + }
63 + }
64 + }
65 + }
66 +
67 + @override
68 + Widget build(BuildContext context) {
69 + return WCCDetailsWidget(
70 + widget.pairing,
71 + expiryDate,
72 + sessionWidgets,
73 + widget.web3walletService,
74 + );
75 + }
76 +}
77 +
78 +class WCCDetailsWidget extends BasePage {
79 + WCCDetailsWidget(
80 + this.pairing,
81 + this.expiryDate,
82 + this.sessionWidgets,
83 + this.web3walletService,
84 + );
85 +
86 + final PairingInfo pairing;
87 + final String expiryDate;
88 + final List<Widget> sessionWidgets;
89 + final Web3WalletService web3walletService;
90 +
91 + @override
92 + Widget body(BuildContext context) {
93 + return Scaffold(
94 + body: SingleChildScrollView(
95 + child: Container(
96 + padding: const EdgeInsets.all(8),
97 + child: Column(
98 + mainAxisSize: MainAxisSize.min,
99 + children: [
100 + Flexible(
101 + child: CircleAvatar(
102 + backgroundImage: (pairing.peerMetadata!.icons.isNotEmpty
103 + ? NetworkImage(pairing.peerMetadata!.icons[0])
104 + : const AssetImage('assets/images/default_icon.png'))
105 + as ImageProvider<Object>,
106 + ),
107 + ),
108 + const SizedBox(height: 20.0),
109 + Text(
110 + pairing.peerMetadata!.name,
111 + style: TextStyle(
112 + fontSize: 16.0,
113 + fontWeight: FontWeight.w500,
114 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
115 + ),
116 + ),
117 + const SizedBox(height: 16.0),
118 + Text(
119 + pairing.peerMetadata!.url,
120 + style: TextStyle(
121 + fontSize: 14.0,
122 + fontWeight: FontWeight.normal,
123 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
124 + ),
125 + ),
126 + const SizedBox(height: 8.0),
127 + Text(
128 + '${S.current.expiresOn}: $expiryDate',
129 + style: TextStyle(
130 + fontSize: 14.0,
131 + fontWeight: FontWeight.normal,
132 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
133 + ),
134 + ),
135 + const SizedBox(height: 20.0),
136 + Column(
137 + mainAxisAlignment: MainAxisAlignment.start,
138 + crossAxisAlignment: CrossAxisAlignment.center,
139 + children: sessionWidgets,
140 + ),
141 + const SizedBox(height: 20.0),
142 + PrimaryButton(
143 + onPressed: () =>
144 + _onDeleteButtonPressed(context, pairing.peerMetadata!.name, web3walletService),
145 + text: S.current.delete,
146 + color: Theme.of(context).primaryColor,
147 + textColor: Colors.white,
148 + ),
149 + ],
150 + ),
151 + ),
152 + ),
153 + );
154 + }
155 +
156 + Future<void> _onDeleteButtonPressed(
157 + BuildContext context, String dAppName, Web3WalletService web3walletService) async {
158 + bool confirmed = false;
159 +
160 + await showPopUp<void>(
161 + context: context,
162 + builder: (BuildContext dialogContext) {
163 + return AlertWithTwoActions(
164 + alertTitle: S.of(context).delete,
165 + alertContent: '${S.current.deleteConnectionConfirmationPrompt} $dAppName?',
166 + leftButtonText: S.of(context).cancel,
167 + rightButtonText: S.of(context).delete,
168 + actionLeftButton: () => Navigator.of(dialogContext).pop(),
169 + actionRightButton: () {
170 + confirmed = true;
171 + Navigator.of(dialogContext).pop();
172 + },
173 + );
174 + },
175 + );
176 + if (confirmed) {
177 + try {
178 + await web3walletService.disconnectSession(pairing.topic);
179 +
180 + Navigator.of(context).pop();
181 + } catch (e) {
182 + log(e.toString());
183 + }
184 + }
185 + }
186 +}
lib/src/screens/wallet_connect/widgets/connection_item_widget.dart new
+102
@@ -0,0 +1,102 @@
1 +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
2 +import 'package:flutter/material.dart';
3 +import '../../../../core/wallet_connect/models/connection_model.dart';
4 +
5 +class ConnectionItemWidget extends StatelessWidget {
6 + const ConnectionItemWidget({required this.model, Key? key}) : super(key: key);
7 +
8 + final ConnectionModel model;
9 +
10 + @override
11 + Widget build(BuildContext context) {
12 +
13 + return Container(
14 + width: double.infinity,
15 + decoration: BoxDecoration(
16 + color: Theme.of(context).cardColor,
17 + borderRadius: BorderRadius.circular(8),
18 + ),
19 + padding: const EdgeInsets.all(8),
20 + margin: const EdgeInsetsDirectional.only(top: 8),
21 + child: Visibility(
22 + visible: model.elements != null,
23 + child: Column(
24 + crossAxisAlignment: CrossAxisAlignment.start,
25 + children: [
26 + Text(
27 + model.title ?? '',
28 + style: TextStyle(
29 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
30 + fontSize: 14,
31 + fontWeight: FontWeight.w600,
32 + ),
33 + ),
34 + const SizedBox(height: 8),
35 + if (model.elements != null)
36 + Wrap(
37 + spacing: 4,
38 + runSpacing: 4,
39 + direction: Axis.horizontal,
40 + children: model.elements!
41 + .map((e) => _ModelElementWidget(model: model, modelElement: e))
42 + .toList(),
43 + ),
44 + ],
45 + ),
46 + replacement: _NoModelElementWidget(model: model),
47 + ),
48 + );
49 + }
50 +}
51 +
52 +class _NoModelElementWidget extends StatelessWidget {
53 + const _NoModelElementWidget({required this.model});
54 +
55 + final ConnectionModel model;
56 +
57 + @override
58 + Widget build(BuildContext context) {
59 + return Text(
60 + model.text!,
61 + style: TextStyle(
62 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
63 + fontSize: 14,
64 + fontWeight: FontWeight.w600,
65 + ),
66 + );
67 + }
68 +}
69 +
70 +class _ModelElementWidget extends StatelessWidget {
71 + const _ModelElementWidget({
72 + required this.model,
73 + required this.modelElement,
74 + });
75 +
76 + final ConnectionModel model;
77 + final String modelElement;
78 +
79 + @override
80 + Widget build(BuildContext context) {
81 + return InkWell(
82 + onTap: model.elementActions != null ? model.elementActions![modelElement] : null,
83 + child: Container(
84 + decoration: BoxDecoration(
85 + color: Theme.of(context).colorScheme.background,
86 + borderRadius: BorderRadius.circular(6),
87 + ),
88 + padding: const EdgeInsets.all(8),
89 + child: Text(
90 + modelElement,
91 + style: TextStyle(
92 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
93 + fontSize: 14,
94 + fontWeight: FontWeight.w600,
95 + ),
96 + maxLines: 10,
97 + overflow: TextOverflow.ellipsis,
98 + ),
99 + ),
100 + );
101 + }
102 +}
lib/src/screens/wallet_connect/widgets/connection_request_widget.dart new
+166
@@ -0,0 +1,166 @@
1 +// ignore_for_file: public_member_api_docs, sort_constructors_first
2 +import 'package:cake_wallet/generated/i18n.dart';
3 +import 'package:flutter/material.dart';
4 +import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
5 +
6 +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
7 +
8 +import '../../../../core/wallet_connect/models/auth_request_model.dart';
9 +import '../../../../core/wallet_connect/models/connection_model.dart';
10 +import '../../../../core/wallet_connect/models/session_request_model.dart';
11 +import '../utils/namespace_model_builder.dart';
12 +import 'connection_widget.dart';
13 +
14 +class ConnectionRequestWidget extends StatefulWidget {
15 + const ConnectionRequestWidget({
16 + required this.wallet,
17 + this.authRequest,
18 + this.sessionProposal,
19 + Key? key,
20 + }) : super(key: key);
21 +
22 + final Web3Wallet wallet;
23 + final AuthRequestModel? authRequest;
24 + final SessionRequestModel? sessionProposal;
25 +
26 + @override
27 + State<ConnectionRequestWidget> createState() => _ConnectionRequestWidgetState();
28 +}
29 +
30 +class _ConnectionRequestWidgetState extends State<ConnectionRequestWidget> {
31 + ConnectionMetadata? metadata;
32 +
33 + @override
34 + void initState() {
35 + super.initState();
36 + // Get the connection metadata
37 + metadata = widget.authRequest?.request.requester ?? widget.sessionProposal?.request.proposer;
38 + }
39 +
40 + @override
41 + Widget build(BuildContext context) {
42 + if (metadata == null) {
43 + return Text(
44 + S.current.error,
45 + style: TextStyle(
46 + fontSize: 14,
47 + fontWeight: FontWeight.normal,
48 + color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
49 + ),
50 + );
51 + }
52 +
53 + return _ConnectionMetadataDisplayWidget(
54 + metadata: metadata,
55 + authRequest: widget.authRequest,
56 + sessionProposal: widget.sessionProposal,
57 + wallet: widget.wallet,
58 + );
59 + }
60 +}
61 +
62 +class _ConnectionMetadataDisplayWidget extends StatelessWidget {
63 + const _ConnectionMetadataDisplayWidget({
64 + required this.metadata,
65 + required this.wallet,
66 + this.authRequest,
67 + required this.sessionProposal,
68 + });
69 +
70 + final ConnectionMetadata? metadata;
71 + final Web3Wallet wallet;
72 + final AuthRequestModel? authRequest;
73 + final SessionRequestModel? sessionProposal;
74 +
75 + @override
76 + Widget build(BuildContext context) {
77 + return Container(
78 + decoration: BoxDecoration(
79 + color: Color.fromARGB(255, 18, 18, 19),
80 + borderRadius: BorderRadius.circular(8),
81 + ),
82 + child: Column(
83 + crossAxisAlignment: CrossAxisAlignment.center,
84 + mainAxisSize: MainAxisSize.min,
85 + children: [
86 + Text(
87 + metadata!.metadata.name,
88 + style: TextStyle(
89 + fontSize: 16,
90 + fontWeight: FontWeight.normal,
91 + color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
92 + ),
93 + textAlign: TextAlign.center,
94 + ),
95 + Text(
96 + S.current.wouoldLikeToConnect,
97 + style: TextStyle(
98 + fontSize: 14,
99 + fontWeight: FontWeight.normal,
100 + color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
101 + ),
102 + textAlign: TextAlign.center,
103 + ),
104 + const SizedBox(height: 8),
105 + Text(
106 + metadata!.metadata.url,
107 + style: TextStyle(
108 + fontSize: 16.0,
109 + fontWeight: FontWeight.normal,
110 + color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
111 + ),
112 + textAlign: TextAlign.center,
113 + ),
114 + const SizedBox(height: 8),
115 + Visibility(
116 + visible: authRequest != null,
117 + child: _AuthRequestWidget(wallet: wallet, authRequest: authRequest),
118 +
119 + //If authRequest is null, sessionProposal is not null.
120 + replacement: _SessionProposalWidget(sessionProposal: sessionProposal!),
121 + ),
122 + ],
123 + ),
124 + );
125 + }
126 +}
127 +
128 +class _AuthRequestWidget extends StatelessWidget {
129 + const _AuthRequestWidget({required this.wallet, this.authRequest});
130 +
131 + final Web3Wallet wallet;
132 + final AuthRequestModel? authRequest;
133 +
134 + @override
135 + Widget build(BuildContext context) {
136 + final model = ConnectionModel(
137 + text: wallet.formatAuthMessage(
138 + iss: 'did:pkh:eip155:1:${authRequest!.iss}',
139 + cacaoPayload: CacaoRequestPayload.fromPayloadParams(
140 + authRequest!.request.payloadParams,
141 + ),
142 + ),
143 + );
144 + return ConnectionWidget(
145 + title: S.current.message,
146 + info: [model],
147 + );
148 + }
149 +}
150 +
151 +class _SessionProposalWidget extends StatelessWidget {
152 + const _SessionProposalWidget({required this.sessionProposal});
153 +
154 + final SessionRequestModel sessionProposal;
155 +
156 + @override
157 + Widget build(BuildContext context) {
158 + // Create the connection models using the required and optional namespaces provided by the proposal data
159 + // The key is the title and the list of values is the data
160 + final List<ConnectionWidget> views = ConnectionWidgetBuilder.buildFromRequiredNamespaces(
161 + sessionProposal.request.requiredNamespaces,
162 + );
163 +
164 + return Column(children: views);
165 + }
166 +}
lib/src/screens/wallet_connect/widgets/connection_widget.dart new
+45
@@ -0,0 +1,45 @@
1 +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
2 +import 'package:flutter/material.dart';
3 +
4 +import '../../../../core/wallet_connect/models/connection_model.dart';
5 +import 'connection_item_widget.dart';
6 +
7 +class ConnectionWidget extends StatelessWidget {
8 + const ConnectionWidget({required this.title, required this.info, super.key});
9 +
10 + final String title;
11 + final List<ConnectionModel> info;
12 +
13 + @override
14 + Widget build(BuildContext context) {
15 + return Container(
16 + decoration: BoxDecoration(
17 + color: Theme.of(context).primaryColorLight,
18 + borderRadius: BorderRadius.circular(8),
19 + ),
20 + padding: const EdgeInsets.all(8),
21 + child: Column(
22 + crossAxisAlignment: CrossAxisAlignment.start,
23 + children: [
24 + Container(
25 + decoration: BoxDecoration(
26 + color: Theme.of(context).colorScheme.background,
27 + borderRadius: BorderRadius.circular(8),
28 + ),
29 + padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8),
30 + child: Text(
31 + title,
32 + style: TextStyle(
33 + fontSize: 16,
34 + fontWeight: FontWeight.w600,
35 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
36 + ),
37 + ),
38 + ),
39 + const SizedBox(height: 8),
40 + ...info.map((e) => ConnectionItemWidget(model: e)),
41 + ],
42 + ),
43 + );
44 + }
45 +}
lib/src/screens/wallet_connect/widgets/error_display_widget.dart new
+36
@@ -0,0 +1,36 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:flutter/material.dart';
3 +
4 +class BottomSheetMessageDisplayWidget extends StatelessWidget {
5 + final String message;
6 + final bool isError;
7 +
8 + const BottomSheetMessageDisplayWidget({super.key, required this.message, this.isError = true});
9 +
10 + @override
11 + Widget build(BuildContext context) {
12 + return Column(
13 + mainAxisSize: MainAxisSize.min,
14 + crossAxisAlignment: CrossAxisAlignment.start,
15 + children: [
16 + Text(
17 + isError ? S.current.error : S.current.successful,
18 + style: TextStyle(
19 + fontSize: 16,
20 + fontWeight: FontWeight.normal,
21 + color: Colors.white,
22 + ),
23 + ),
24 + SizedBox(height: 8),
25 + Text(
26 + message,
27 + style: TextStyle(
28 + fontSize: 14,
29 + fontWeight: FontWeight.normal,
30 + color: Colors.white,
31 + ),
32 + ),
33 + ],
34 + );
35 + }
36 +}
lib/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart new
+62
@@ -0,0 +1,62 @@
1 +import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
2 +import 'package:flutter/material.dart';
3 +
4 +import '../../../../../core/wallet_connect/models/bottom_sheet_queue_item_model.dart';
5 +
6 +class BottomSheetListener extends StatefulWidget {
7 + final BottomSheetService bottomSheetService;
8 + final Widget child;
9 +
10 + const BottomSheetListener({
11 + required this.child,
12 + required this.bottomSheetService,
13 + super.key,
14 + });
15 +
16 + @override
17 + BottomSheetListenerState createState() => BottomSheetListenerState();
18 +}
19 +
20 +class BottomSheetListenerState extends State<BottomSheetListener> {
21 +
22 + @override
23 + void initState() {
24 + super.initState();
25 + widget.bottomSheetService.currentSheet.addListener(_showBottomSheet);
26 + }
27 +
28 + @override
29 + void dispose() {
30 + widget.bottomSheetService.currentSheet.removeListener(_showBottomSheet);
31 + super.dispose();
32 + }
33 +
34 + Future<void> _showBottomSheet() async {
35 + if (widget.bottomSheetService.currentSheet.value != null) {
36 + BottomSheetQueueItemModel item = widget.bottomSheetService.currentSheet.value!;
37 + final value = await showModalBottomSheet(
38 + context: context,
39 + isDismissible: item.isModalDismissible,
40 + backgroundColor: Color.fromARGB(0, 0, 0, 0),
41 + isScrollControlled: true,
42 + constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.9),
43 + builder: (context) {
44 + return Container(
45 + decoration: const BoxDecoration(
46 + color: Color.fromARGB(255, 18, 18, 19),
47 + borderRadius: BorderRadius.all(Radius.circular(16)),
48 + ),
49 + padding: const EdgeInsets.all(16),
50 + margin: const EdgeInsets.all(16),
51 + child: item.widget,
52 + );
53 + },
54 + );
55 + item.completer.complete(value);
56 + widget.bottomSheetService.resetCurrentSheet();
57 + }
58 + }
59 +
60 + @override
61 + Widget build(BuildContext context) => widget.child;
62 +}
lib/src/screens/wallet_connect/widgets/modals/web3_request_modal.dart new
+48
@@ -0,0 +1,48 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:cake_wallet/src/widgets/primary_button.dart';
3 +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
4 +import 'package:flutter/material.dart';
5 +
6 +class Web3RequestModal extends StatelessWidget {
7 + const Web3RequestModal({required this.child, this.onAccept, this.onReject, super.key});
8 +
9 + final Widget child;
10 + final VoidCallback? onAccept;
11 + final VoidCallback? onReject;
12 +
13 + @override
14 + Widget build(BuildContext context) {
15 + return SingleChildScrollView(
16 + child: Column(
17 + mainAxisSize: MainAxisSize.min,
18 + children: [
19 + child,
20 + const SizedBox(height: 16),
21 + Row(
22 + mainAxisAlignment: MainAxisAlignment.spaceEvenly,
23 + children: [
24 +
25 + Expanded(
26 + child: PrimaryButton(
27 + onPressed: onReject ?? () => Navigator.of(context).pop(false),
28 + text: S.current.reject,
29 + color: Theme.of(context).colorScheme.error,
30 + textColor: Theme.of(context).colorScheme.onError,
31 + ),
32 + ),
33 + const SizedBox(width: 16),
34 + Expanded(
35 + child: PrimaryButton(
36 + onPressed: onAccept ?? () => Navigator.of(context).pop(true),
37 + text: S.current.approve,
38 + color: Theme.of(context).primaryColor,
39 + textColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
40 + ),
41 + ),
42 + ],
43 + ),
44 + ],
45 + ),
46 + );
47 + }
48 +}
lib/src/screens/wallet_connect/widgets/pairing_item_widget.dart new
+82
@@ -0,0 +1,82 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
3 +import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
4 +import 'package:flutter/material.dart';
5 +import 'package:walletconnect_flutter_v2/apis/core/pairing/utils/pairing_models.dart';
6 +
7 +class PairingItemWidget extends StatelessWidget {
8 + const PairingItemWidget({required this.pairing, required this.onTap, super.key});
9 +
10 + final PairingInfo pairing;
11 + final void Function() onTap;
12 +
13 + @override
14 + Widget build(BuildContext context) {
15 + PairingMetadata? metadata = pairing.peerMetadata;
16 + if (metadata == null) {
17 + return SizedBox.shrink();
18 + }
19 +
20 + DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(pairing.expiry * 1000);
21 + int year = dateTime.year;
22 + int month = dateTime.month;
23 + int day = dateTime.day;
24 +
25 + String expiryDate =
26 + '$year-${month.toString().padLeft(2, '0')}-${day.toString().padLeft(2, '0')}';
27 +
28 + return ListTile(
29 + leading: CircleAvatar(
30 + backgroundImage: (metadata.icons.isNotEmpty
31 + ? NetworkImage(metadata.icons[0])
32 + : const AssetImage(
33 + 'assets/images/default_icon.png',
34 + )) as ImageProvider<Object>,
35 + ),
36 + title: Text(
37 + metadata.name,
38 + style: TextStyle(
39 + fontSize: 16.0,
40 + fontWeight: FontWeight.w700,
41 + color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
42 + ),
43 + ),
44 + subtitle: Column(
45 + crossAxisAlignment: CrossAxisAlignment.start,
46 + children: [
47 + Text(
48 + metadata.url,
49 + style: TextStyle(
50 + fontSize: 14.0,
51 + fontWeight: FontWeight.w700,
52 + color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
53 + ),
54 + ),
55 + Text(
56 + '${S.current.expiresOn}: $expiryDate',
57 + style: TextStyle(
58 + fontSize: 14.0,
59 + fontWeight: FontWeight.w700,
60 + color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
61 + ),
62 + ),
63 + ],
64 + ),
65 + trailing: Container(
66 + height: 40,
67 + width: 44,
68 + padding: EdgeInsets.all(10),
69 + decoration: BoxDecoration(
70 + shape: BoxShape.circle,
71 + color: Theme.of(context).extension<ReceivePageTheme>()!.iconsBackgroundColor,
72 + ),
73 + child: Icon(
74 + Icons.edit,
75 + size: 14,
76 + color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
77 + ),
78 + ),
79 + onTap: onTap,
80 + );
81 + }
82 +}
lib/store/settings_store.dart
+1 -2
@@ -595,8 +595,7 @@ abstract class SettingsStoreBase with Store {
595 SortBalanceBy.values[sharedPreferences.getInt(PreferencesKey.sortBalanceBy) ?? 0];
596 final pinNativeTokenAtTop =
597 sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop) ?? true;
598 - final useEtherscan =
599 - sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
598 + final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
599
600 // If no value
601 if (pinLength == null || pinLength == 0) {
lib/view_model/dashboard/dashboard_view_model.dart
+126 -130
@@ -47,70 +47,70 @@ abstract class DashboardViewModelBase with Store {
47 required this.yatStore,
48 required this.ordersStore,
49 required this.anonpayTransactionsStore})
50 - : isOutdatedElectrumWallet = false,
51 - hasSellAction = false,
52 - hasBuyAction = false,
53 - hasExchangeAction = false,
54 - isShowFirstYatIntroduction = false,
55 - isShowSecondYatIntroduction = false,
56 - isShowThirdYatIntroduction = false,
57 - filterItems = {
58 - S.current.transactions: [
59 - FilterItem(
60 - value: () => transactionFilterStore.displayAll,
61 - caption: S.current.all_transactions,
62 - onChanged: transactionFilterStore.toggleAll),
63 - FilterItem(
64 - value: () => transactionFilterStore.displayIncoming,
65 - caption: S.current.incoming,
66 - onChanged:transactionFilterStore.toggleIncoming),
67 - FilterItem(
68 - value: () => transactionFilterStore.displayOutgoing,
69 - caption: S.current.outgoing,
70 - onChanged: transactionFilterStore.toggleOutgoing),
71 - // FilterItem(
72 - // value: () => false,
73 - // caption: S.current.transactions_by_date,
74 - // onChanged: null),
75 - ],
76 - S.current.trades: [
77 - FilterItem(
78 - value: () => tradeFilterStore.displayAllTrades,
79 - caption: S.current.all_trades,
80 - onChanged: () => tradeFilterStore
81 - .toggleDisplayExchange(ExchangeProviderDescription.all)),
82 - FilterItem(
83 - value: () => tradeFilterStore.displayChangeNow,
84 - caption: ExchangeProviderDescription.changeNow.title,
85 - onChanged: () => tradeFilterStore
86 - .toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
87 - FilterItem(
88 - value: () => tradeFilterStore.displaySideShift,
89 - caption: ExchangeProviderDescription.sideShift.title,
90 - onChanged: () => tradeFilterStore
91 - .toggleDisplayExchange(ExchangeProviderDescription.sideShift)),
92 - FilterItem(
93 - value: () => tradeFilterStore.displaySimpleSwap,
94 - caption: ExchangeProviderDescription.simpleSwap.title,
95 - onChanged: () => tradeFilterStore
96 - .toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)),
97 - FilterItem(
98 - value: () => tradeFilterStore.displayTrocador,
99 - caption: ExchangeProviderDescription.trocador.title,
100 - onChanged: () => tradeFilterStore
101 - .toggleDisplayExchange(ExchangeProviderDescription.trocador)),
102 - FilterItem(
103 - value: () => tradeFilterStore.displayExolix,
104 - caption: ExchangeProviderDescription.exolix.title,
105 - onChanged: () => tradeFilterStore
106 - .toggleDisplayExchange(ExchangeProviderDescription.exolix)),
107 - ]
108 - },
109 - subname = '',
110 - name = appStore.wallet!.name,
111 - type = appStore.wallet!.type,
112 - transactions = ObservableList<TransactionListItem>(),
113 - wallet = appStore.wallet! {
50 + : isOutdatedElectrumWallet = false,
51 + hasSellAction = false,
52 + hasBuyAction = false,
53 + hasExchangeAction = false,
54 + isShowFirstYatIntroduction = false,
55 + isShowSecondYatIntroduction = false,
56 + isShowThirdYatIntroduction = false,
57 + filterItems = {
58 + S.current.transactions: [
59 + FilterItem(
60 + value: () => transactionFilterStore.displayAll,
61 + caption: S.current.all_transactions,
62 + onChanged: transactionFilterStore.toggleAll),
63 + FilterItem(
64 + value: () => transactionFilterStore.displayIncoming,
65 + caption: S.current.incoming,
66 + onChanged: transactionFilterStore.toggleIncoming),
67 + FilterItem(
68 + value: () => transactionFilterStore.displayOutgoing,
69 + caption: S.current.outgoing,
70 + onChanged: transactionFilterStore.toggleOutgoing),
71 + // FilterItem(
72 + // value: () => false,
73 + // caption: S.current.transactions_by_date,
74 + // onChanged: null),
75 + ],
76 + S.current.trades: [
77 + FilterItem(
78 + value: () => tradeFilterStore.displayAllTrades,
79 + caption: S.current.all_trades,
80 + onChanged: () =>
81 + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.all)),
82 + FilterItem(
83 + value: () => tradeFilterStore.displayChangeNow,
84 + caption: ExchangeProviderDescription.changeNow.title,
85 + onChanged: () =>
86 + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
87 + FilterItem(
88 + value: () => tradeFilterStore.displaySideShift,
89 + caption: ExchangeProviderDescription.sideShift.title,
90 + onChanged: () =>
91 + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.sideShift)),
92 + FilterItem(
93 + value: () => tradeFilterStore.displaySimpleSwap,
94 + caption: ExchangeProviderDescription.simpleSwap.title,
95 + onChanged: () =>
96 + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)),
97 + FilterItem(
98 + value: () => tradeFilterStore.displayTrocador,
99 + caption: ExchangeProviderDescription.trocador.title,
100 + onChanged: () =>
101 + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.trocador)),
102 + FilterItem(
103 + value: () => tradeFilterStore.displayExolix,
104 + caption: ExchangeProviderDescription.exolix.title,
105 + onChanged: () =>
106 + tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.exolix)),
107 + ]
108 + },
109 + subname = '',
110 + name = appStore.wallet!.name,
111 + type = appStore.wallet!.type,
112 + transactions = ObservableList<TransactionListItem>(),
113 + wallet = appStore.wallet! {
114 name = wallet.name;
115 type = wallet.type;
116 isOutdatedElectrumWallet =
@@ -125,15 +125,17 @@ abstract class DashboardViewModelBase with Store {
125 if (_wallet.type == WalletType.monero) {
126 subname = monero!.getCurrentAccount(_wallet).label;
127
128 - _onMoneroAccountChangeReaction = reaction((_) => monero!.getMoneroWalletDetails(wallet)
129 - .account, (Account account) => _onMoneroAccountChange(_wallet));
128 + _onMoneroAccountChangeReaction = reaction(
129 + (_) => monero!.getMoneroWalletDetails(wallet).account,
130 + (Account account) => _onMoneroAccountChange(_wallet));
131
131 - _onMoneroBalanceChangeReaction = reaction((_) => monero!.getMoneroWalletDetails(wallet).balance,
132 + _onMoneroBalanceChangeReaction = reaction(
133 + (_) => monero!.getMoneroWalletDetails(wallet).balance,
134 (MoneroBalance balance) => _onMoneroTransactionsUpdate(_wallet));
135
134 - final _accountTransactions = _wallet
135 - .transactionHistory.transactions.values
136 - .where((tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
136 + final _accountTransactions = _wallet.transactionHistory.transactions.values
137 + .where((tx) =>
138 + monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
139 .toList();
140
141 transactions = ObservableList.of(_accountTransactions.map((transaction) =>
@@ -142,34 +144,33 @@ abstract class DashboardViewModelBase with Store {
144 balanceViewModel: balanceViewModel,
145 settingsStore: appStore.settingsStore)));
146 } else {
145 - transactions = ObservableList.of(wallet
146 - .transactionHistory.transactions.values
147 - .map((transaction) => TransactionListItem(
147 + transactions = ObservableList.of(wallet.transactionHistory.transactions.values.map(
148 + (transaction) => TransactionListItem(
149 transaction: transaction,
150 balanceViewModel: balanceViewModel,
151 settingsStore: appStore.settingsStore)));
152 }
153
154 reaction((_) => appStore.wallet, _onWalletChange);
154 -
155 +
156 connectMapToListWithTransform(
157 appStore.wallet!.transactionHistory.transactions,
158 transactions,
159 (TransactionInfo? transaction) => TransactionListItem(
160 transaction: transaction!,
161 balanceViewModel: balanceViewModel,
161 - settingsStore: appStore.settingsStore),
162 - filter: (TransactionInfo? transaction) {
163 - if (transaction == null) {
164 - return false;
165 - }
166 -
167 - final wallet = _wallet;
168 - if (wallet.type == WalletType.monero) {
169 - return monero!.getTransactionInfoAccountId(transaction) == monero!.getCurrentAccount(wallet).id;
170 - }
171 -
172 - return true;
162 + settingsStore: appStore.settingsStore), filter: (TransactionInfo? transaction) {
163 + if (transaction == null) {
164 + return false;
165 + }
166 +
167 + final wallet = _wallet;
168 + if (wallet.type == WalletType.monero) {
169 + return monero!.getTransactionInfoAccountId(transaction) ==
170 + monero!.getCurrentAccount(wallet).id;
171 + }
172 +
173 + return true;
174 });
175 }
176
@@ -216,24 +217,21 @@ abstract class DashboardViewModelBase with Store {
217 }
218
219 @computed
219 - BalanceDisplayMode get balanceDisplayMode =>
220 - appStore.settingsStore.balanceDisplayMode;
221 -
220 + BalanceDisplayMode get balanceDisplayMode => appStore.settingsStore.balanceDisplayMode;
221 +
222 @computed
223 bool get shouldShowMarketPlaceInDashboard {
224 return appStore.settingsStore.shouldShowMarketPlaceInDashboard;
225 }
226
227 @computed
228 - List<TradeListItem> get trades => tradesStore.trades
229 - .where((trade) => trade.trade.walletId == wallet.id)
230 - .toList();
228 + List<TradeListItem> get trades =>
229 + tradesStore.trades.where((trade) => trade.trade.walletId == wallet.id).toList();
230
231 @computed
233 - List<OrderListItem> get orders => ordersStore.orders
234 - .where((item) => item.order.walletId == wallet.id)
235 - .toList();
236 -
232 + List<OrderListItem> get orders =>
233 + ordersStore.orders.where((item) => item.order.walletId == wallet.id).toList();
234 +
235 @computed
236 List<AnonpayTransactionListItem> get anonpayTransactons => anonpayTransactionsStore.transactions
237 .where((item) => item.transaction.walletId == wallet.id)
@@ -250,7 +248,8 @@ abstract class DashboardViewModelBase with Store {
248 List<ActionListItem> get items {
249 final _items = <ActionListItem>[];
250
253 - _items.addAll(transactionFilterStore.filtered(transactions: [...transactions, ...anonpayTransactons]));
251 + _items.addAll(
252 + transactionFilterStore.filtered(transactions: [...transactions, ...anonpayTransactons]));
253 _items.addAll(tradeFilterStore.filtered(trades: trades, wallet: wallet));
254 _items.addAll(orders);
255
@@ -258,8 +257,7 @@ abstract class DashboardViewModelBase with Store {
257 }
258
259 @observable
261 - WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
262 - wallet;
260 + WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
261
262 bool get hasRescan => wallet.type == WalletType.monero || wallet.type == WalletType.haven;
263
@@ -283,7 +281,6 @@ abstract class DashboardViewModelBase with Store {
281
282 Map<String, List<FilterItem>> filterItems;
283
286 -
284 BuyProviderType get defaultBuyProvider => settingsStore.defaultBuyProvider;
285
286 bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled;
@@ -291,8 +288,7 @@ abstract class DashboardViewModelBase with Store {
288 bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup;
289
290 @action
294 - void furtherShowYatPopup(bool shouldShow) =>
295 - settingsStore.shouldShowYatPopup = shouldShow;
291 + void furtherShowYatPopup(bool shouldShow) => settingsStore.shouldShowYatPopup = shouldShow;
292
293 @computed
294 bool get isEnabledExchangeAction => settingsStore.exchangeStatus != ExchangeApiMode.disabled;
@@ -301,8 +297,7 @@ abstract class DashboardViewModelBase with Store {
297 bool hasExchangeAction;
298
299 @computed
304 - bool get isEnabledBuyAction =>
305 - !settingsStore.disableBuy && wallet.type != WalletType.haven;
300 + bool get isEnabledBuyAction => !settingsStore.disableBuy && wallet.type != WalletType.haven;
301
302 @observable
303 bool hasBuyAction;
@@ -330,9 +325,7 @@ abstract class DashboardViewModelBase with Store {
325
326 @action
327 void _onWalletChange(
333 - WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
334 - TransactionInfo>?
335 - wallet) {
328 + WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>? wallet) {
329 if (wallet == null) {
330 return;
331 }
@@ -350,10 +343,12 @@ abstract class DashboardViewModelBase with Store {
343 _onMoneroAccountChangeReaction?.reaction.dispose();
344 _onMoneroBalanceChangeReaction?.reaction.dispose();
345
353 - _onMoneroAccountChangeReaction = reaction((_) => monero!.getMoneroWalletDetails(wallet)
354 - .account, (Account account) => _onMoneroAccountChange(wallet));
346 + _onMoneroAccountChangeReaction = reaction(
347 + (_) => monero!.getMoneroWalletDetails(wallet).account,
348 + (Account account) => _onMoneroAccountChange(wallet));
349
356 - _onMoneroBalanceChangeReaction = reaction((_) => monero!.getMoneroWalletDetails(wallet).balance,
350 + _onMoneroBalanceChangeReaction = reaction(
351 + (_) => monero!.getMoneroWalletDetails(wallet).balance,
352 (MoneroBalance balance) => _onMoneroTransactionsUpdate(wallet));
353
354 _onMoneroTransactionsUpdate(wallet);
@@ -364,8 +359,8 @@ abstract class DashboardViewModelBase with Store {
359
360 transactions.clear();
361
367 - transactions.addAll(wallet.transactionHistory.transactions.values.map(
368 - (transaction) => TransactionListItem(
362 + transactions.addAll(wallet.transactionHistory.transactions.values.map((transaction) =>
363 + TransactionListItem(
364 transaction: transaction,
365 balanceViewModel: balanceViewModel,
366 settingsStore: appStore.settingsStore)));
@@ -374,21 +369,19 @@ abstract class DashboardViewModelBase with Store {
369 connectMapToListWithTransform(
370 appStore.wallet!.transactionHistory.transactions,
371 transactions,
377 - (TransactionInfo? transaction)
378 - => TransactionListItem(
372 + (TransactionInfo? transaction) => TransactionListItem(
373 transaction: transaction!,
374 balanceViewModel: balanceViewModel,
381 - settingsStore: appStore.settingsStore),
382 - filter: (TransactionInfo? tx) {
383 - if (tx == null) {
384 - return false;
385 - }
375 + settingsStore: appStore.settingsStore), filter: (TransactionInfo? tx) {
376 + if (tx == null) {
377 + return false;
378 + }
379
387 - if (wallet.type == WalletType.monero) {
388 - return monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id;
389 - }
380 + if (wallet.type == WalletType.monero) {
381 + return monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id;
382 + }
383
391 - return true;
384 + return true;
385 });
386 }
387
@@ -402,15 +395,18 @@ abstract class DashboardViewModelBase with Store {
395 void _onMoneroTransactionsUpdate(WalletBase wallet) {
396 transactions.clear();
397
405 - final _accountTransactions = monero!.getTransactionHistory(wallet).transactions.values
406 - .where((tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
398 + final _accountTransactions = monero!
399 + .getTransactionHistory(wallet)
400 + .transactions
401 + .values
402 + .where(
403 + (tx) => monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
404 .toList();
405
409 - transactions.addAll(_accountTransactions.map((transaction) =>
410 - TransactionListItem(
411 - transaction: transaction,
412 - balanceViewModel: balanceViewModel,
413 - settingsStore: appStore.settingsStore)));
406 + transactions.addAll(_accountTransactions.map((transaction) => TransactionListItem(
407 + transaction: transaction,
408 + balanceViewModel: balanceViewModel,
409 + settingsStore: appStore.settingsStore)));
410 }
411
412 void updateActions() {
lib/view_model/send/send_view_model.dart
+1 -1
@@ -417,7 +417,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
417
418 String translateErrorMessage(String error, WalletType walletType, CryptoCurrency currency,) {
419 if (walletType == WalletType.ethereum || walletType == WalletType.haven) {
420 - if (error.contains('gas required exceeds allowance (0)') || error.contains('insufficient funds for gas')) {
420 + if (error.contains('gas required exceeds allowance') || error.contains('insufficient funds for gas')) {
421 return S.current.do_not_have_enough_gas_asset(currency.toString());
422 }
423 }
pubspec_base.yaml
+2
@@ -82,6 +82,8 @@ dependencies:
82 shared_preferences_android: 2.0.17
83 url_launcher_android: 6.0.24
84 sensitive_clipboard: ^1.0.0
85 + walletconnect_flutter_v2: ^2.1.4
86 + eth_sig_util: ^0.0.9
87 bitcoin_flutter:
88 git:
89 url: https://github.com/cake-tech/bitcoin_flutter.git
res/values/strings_ar.arb
+22 -3
@@ -689,8 +689,27 @@
689 "default_buy_provider": "مزود شراء الافتراضي",
690 "ask_each_time": "اسأل في كل مرة",
691 "buy_provider_unavailable": "مزود حاليا غير متوفر.",
692 -
692 + "signTransaction": " ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ",
693 + "errorGettingCredentials": "ﺩﺎﻤﺘﻋﻻﺍ ﺕﺎﻧﺎﻴﺑ ﻰﻠﻋ ﻝﻮﺼﺤﻟﺍ ءﺎﻨﺛﺃ ﺄﻄﺧ ﺙﺪﺣ :ﻞﺸﻓ",
694 + "errorSigningTransaction": "ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ ءﺎﻨﺛﺃ ﺄﻄﺧ ﺙﺪﺣ",
695 + "pairingInvalidEvent": "ﺢﻟﺎﺻ ﺮﻴﻏ ﺙﺪﺣ ﻥﺍﺮﻗﺇ",
696 + "chains": "ﻞﺳﻼﺴﻟﺍ",
697 + "methods": " ﻕﺮﻃُ",
698 + "events": "ﺙﺍﺪﺣﻷﺍ",
699 + "reject": "ﺾﻓﺮﻳ",
700 + "approve": "ﺪﻤﺘﻌﻳ",
701 + "expiresOn": "ﻲﻓ ﻪﺘﻴﺣﻼﺻ ﻲﻬﺘﻨﺗ",
702 + "walletConnect": "WalletConnect",
703 + "nullURIError": "ﻍﺭﺎﻓ (URI) ﻢﻈﺘﻨﻤﻟﺍ ﺩﺭﺍﻮﻤﻟﺍ ﻑﺮﻌﻣ",
704 + "connectWalletPrompt": "ﺕﻼﻣﺎﻌﻤﻟﺍ ءﺍﺮﺟﻹ WalletConnect ﻊﻣ ﻚﺘﻈﻔﺤﻣ ﻞﻴﺻﻮﺘﺑ ﻢﻗ",
705 + "newConnection": "ﺪﻳﺪﺟ ﻝﺎﺼﺗﺍ",
706 + "activeConnectionsPrompt": "ﺎﻨﻫ ﺔﻄﺸﻨﻟﺍ ﺕﻻﺎﺼﺗﻻﺍ ﺮﻬﻈﺘﺳ",
707 + "deleteConnectionConfirmationPrompt": "ـﺑ ﻝﺎﺼﺗﻻﺍ ﻑﺬﺣ ﺪﻳﺮﺗ ﻚﻧﺃ ﺪﻛﺄﺘﻣ ﺖﻧﺃ ﻞﻫ",
708 + "event": "ﺙﺪﺣ",
709 + "successful": "ﺢﺟﺎﻧ",
710 + "wouoldLikeToConnect": "ﻝﺎﺼﺗﻻﺍ ﻲﻓ ﺐﻏﺮﺗ",
711 + "message": "ﺔﻟﺎﺳﺭ",
712 "do_not_have_enough_gas_asset": "ليس لديك ما يكفي من ${currency} لإجراء معاملة وفقًا لشروط شبكة blockchain الحالية. أنت بحاجة إلى المزيد من ${currency} لدفع رسوم شبكة blockchain، حتى لو كنت ترسل أصلًا مختلفًا.",
694 - "totp_auth_url": " TOTP ﺔﻗﺩﺎﺼﻤﻟ URL ﻥﺍﻮﻨﻋ"
713 + "totp_auth_url": "TOTP ﺔﻗﺩﺎﺼﻤﻟ URL ﻥﺍﻮﻨﻋ",
714 + "awaitDAppProcessing": ".ﺔﺠﻟﺎﻌﻤﻟﺍ ﻦﻣ dApp ﻲﻬﺘﻨﻳ ﻰﺘﺣ ﺭﺎﻈﺘﻧﻻﺍ ﻰﺟﺮﻳ"
715 }
696 -
res/values/strings_bg.arb
+22 -1
@@ -685,6 +685,27 @@
685 "default_buy_provider": "Доставчик по подразбиране купува",
686 "ask_each_time": "Питайте всеки път",
687 "buy_provider_unavailable": "Понастоящем доставчик не е наличен.",
688 + "signTransaction": "Подпишете транзакция",
689 + "errorGettingCredentials": "Неуспешно: Грешка при получаване на идентификационни данни",
690 + "errorSigningTransaction": "Възникна грешка при подписване на транзакция",
691 + "pairingInvalidEvent": "Невалидно събитие при сдвояване",
692 + "chains": "Вериги",
693 + "methods": "Методи",
694 + "events": "събития",
695 + "reject": "Отхвърляне",
696 + "approve": "Одобряване",
697 + "expiresOn": "Изтича на",
698 + "walletConnect": "WalletConnect",
699 + "nullURIError": "URI е нула",
700 + "connectWalletPrompt": "Свържете портфейла си с WalletConnect, за да извършвате транзакции",
701 + "newConnection": "Нова връзка",
702 + "activeConnectionsPrompt": "Тук ще се появят активни връзки",
703 + "deleteConnectionConfirmationPrompt": "Сигурни ли сте, че искате да изтриете връзката към",
704 + "event": "Събитие",
705 + "successful": "Успешен",
706 + "wouoldLikeToConnect": "иска да се свърже",
707 + "message": "Съобщение",
708 "do_not_have_enough_gas_asset": "Нямате достатъчно ${currency}, за да извършите транзакция с текущите условия на блокчейн мрежата. Имате нужда от повече ${currency}, за да платите таксите за блокчейн мрежа, дори ако изпращате различен актив.",
689 - "totp_auth_url": "TOTP AUTH URL"
709 + "totp_auth_url": "TOTP AUTH URL",
710 + "awaitDAppProcessing": "Моля, изчакайте dApp да завърши обработката."
711 }
res/values/strings_cs.arb
+22 -1
@@ -685,6 +685,27 @@
685 "default_buy_provider": "Výchozí poskytovatel nákupu",
686 "ask_each_time": "Zeptejte se pokaždé",
687 "buy_provider_unavailable": "Poskytovatel aktuálně nedostupný.",
688 + "signTransaction": "Podepsat transakci",
689 + "errorGettingCredentials": "Selhalo: Chyba při získávání přihlašovacích údajů",
690 + "errorSigningTransaction": "Při podepisování transakce došlo k chybě",
691 + "pairingInvalidEvent": "Neplatná událost párování",
692 + "chains": "Řetězy",
693 + "methods": "Metody",
694 + "events": "Události",
695 + "reject": "Odmítnout",
696 + "approve": "Schvalovat",
697 + "expiresOn": "Vyprší dne",
698 + "walletConnect": "WalletConnect",
699 + "nullURIError": "URI je nulové",
700 + "connectWalletPrompt": "Propojte svou peněženku s WalletConnect a provádějte transakce",
701 + "newConnection": "Nové připojení",
702 + "activeConnectionsPrompt": "Zde se zobrazí aktivní připojení",
703 + "deleteConnectionConfirmationPrompt": "Jste si jisti, že chcete smazat připojení k?",
704 + "event": "událost",
705 + "successful": "Úspěšný",
706 + "wouoldLikeToConnect": "by se chtělo připojit",
707 + "message": "Zpráva",
708 "do_not_have_enough_gas_asset": "Nemáte dostatek ${currency} k provedení transakce s aktuálními podmínkami blockchainové sítě. K placení poplatků za blockchainovou síť potřebujete více ${currency}, i když posíláte jiné aktivum.",
689 - "totp_auth_url": "URL AUTH TOTP"
709 + "totp_auth_url": "URL AUTH TOTP",
710 + "awaitDAppProcessing": "Počkejte, až dApp dokončí zpracování."
711 }
res/values/strings_de.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "Standard-Kaufanbieter",
694 "ask_each_time": "Jedes Mal fragen",
695 "buy_provider_unavailable": "Anbieter derzeit nicht verfügbar.",
696 + "signTransaction": "Transaktion unterzeichnen",
697 + "errorGettingCredentials": "Fehlgeschlagen: Fehler beim Abrufen der Anmeldeinformationen",
698 + "errorSigningTransaction": "Beim Signieren der Transaktion ist ein Fehler aufgetreten",
699 + "pairingInvalidEvent": "Paarung ungültiges Ereignis",
700 + "chains": "Ketten",
701 + "methods": "Methoden",
702 + "events": "Veranstaltungen",
703 + "reject": "Ablehnen",
704 + "approve": "Genehmigen",
705 + "expiresOn": "Läuft aus am",
706 + "walletConnect": "WalletConnect",
707 + "nullURIError": "URI ist null",
708 + "connectWalletPrompt": "Verbinden Sie Ihr Wallet mit WalletConnect, um Transaktionen durchzuführen",
709 + "newConnection": "Neue Verbindung",
710 + "activeConnectionsPrompt": "Hier werden aktive Verbindungen angezeigt",
711 + "deleteConnectionConfirmationPrompt": "Sind Sie sicher, dass Sie die Verbindung zu löschen möchten?",
712 + "event": "Ereignis",
713 + "successful": "Erfolgreich",
714 + "wouoldLikeToConnect": "möchte mich gerne vernetzen",
715 + "message": "Nachricht",
716 "do_not_have_enough_gas_asset": "Sie verfügen nicht über genügend ${currency}, um eine Transaktion unter den aktuellen Bedingungen des Blockchain-Netzwerks durchzuführen. Sie benötigen mehr ${currency}, um die Gebühren für das Blockchain-Netzwerk zu bezahlen, auch wenn Sie einen anderen Vermögenswert senden.",
697 - "totp_auth_url": "TOTP-Auth-URL"
717 + "totp_auth_url": "TOTP-Auth-URL",
718 + "awaitDAppProcessing": "Bitte warten Sie, bis die dApp die Verarbeitung abgeschlossen hat."
719 }
res/values/strings_en.arb
+22 -1
@@ -694,6 +694,27 @@
694 "ask_each_time": "Ask each time",
695 "robinhood_option_description": "Buy and transfer instantly using your debit card, bank account, or Robinhood balance. USA only.",
696 "buy_provider_unavailable": "Provider currently unavailable.",
697 + "signTransaction": "Sign Transaction",
698 + "errorGettingCredentials": "Failed: Error while getting credentials",
699 + "errorSigningTransaction": "An error has occured while signing transaction",
700 + "pairingInvalidEvent": "Pairing Invalid Event",
701 + "chains": "Chains",
702 + "methods": "Methods",
703 + "events": "Events",
704 + "reject": "Reject",
705 + "approve": "Approve",
706 + "expiresOn": "Expires on",
707 + "walletConnect": "WalletConnect",
708 + "nullURIError": "URI is null",
709 + "connectWalletPrompt": "Connect your wallet with WalletConnect to make transactions",
710 + "newConnection": "New Connection",
711 + "activeConnectionsPrompt": "Active connections will appear here",
712 + "deleteConnectionConfirmationPrompt": "Are you sure that you want to delete the connection to",
713 + "event": "Event",
714 + "successful": "Successful",
715 + "wouoldLikeToConnect": "would like to connect",
716 + "message": "Message",
717 "do_not_have_enough_gas_asset": "You do not have enough ${currency} to make a transaction with the current blockchain network conditions. You need more ${currency} to pay blockchain network fees, even if you are sending a different asset.",
698 - "totp_auth_url": "TOTP AUTH URL"
718 + "totp_auth_url": "TOTP AUTH URL",
719 + "awaitDAppProcessing": "Kindly wait for the dApp to finish processing."
720 }
res/values/strings_es.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "Proveedor de compra predeterminado",
694 "ask_each_time": "Pregunta cada vez",
695 "buy_provider_unavailable": "Proveedor actualmente no disponible.",
696 + "signTransaction": "Firmar transacción",
697 + "errorGettingCredentials": "Error: error al obtener las credenciales",
698 + "errorSigningTransaction": "Se ha producido un error al firmar la transacción.",
699 + "pairingInvalidEvent": "Evento de emparejamiento no válido",
700 + "chains": "Cadenas",
701 + "methods": "Métodos",
702 + "events": "Eventos",
703 + "reject": "Rechazar",
704 + "approve": "Aprobar",
705 + "expiresOn": "Expira el",
706 + "walletConnect": "MonederoConectar",
707 + "nullURIError": "URI es nula",
708 + "connectWalletPrompt": "Conecte su billetera con WalletConnect para realizar transacciones",
709 + "newConnection": "Nueva conexión",
710 + "activeConnectionsPrompt": "Las conexiones activas aparecerán aquí",
711 + "deleteConnectionConfirmationPrompt": "¿Está seguro de que desea eliminar la conexión a",
712 + "event": "Evento",
713 + "successful": "Exitoso",
714 + "wouoldLikeToConnect": "quisiera conectar",
715 + "message": "Mensaje",
716 "do_not_have_enough_gas_asset": "No tienes suficiente ${currency} para realizar una transacción con las condiciones actuales de la red blockchain. Necesita más ${currency} para pagar las tarifas de la red blockchain, incluso si envía un activo diferente.",
697 - "totp_auth_url": "URL de autenticación TOTP"
717 + "totp_auth_url": "URL de autenticación TOTP",
718 + "awaitDAppProcessing": "Espere a que la dApp termine de procesarse."
719 }
res/values/strings_fr.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "Fournisseur d'achat par défaut",
694 "ask_each_time": "Demandez à chaque fois",
695 "buy_provider_unavailable": "Fournisseur actuellement indisponible.",
696 + "signTransaction": "Signer une transaction",
697 + "errorGettingCredentials": "Échec : erreur lors de l'obtention des informations d'identification",
698 + "errorSigningTransaction": "Une erreur s'est produite lors de la signature de la transaction",
699 + "pairingInvalidEvent": "Événement de couplage non valide",
700 + "chains": "Chaînes",
701 + "methods": "Méthodes",
702 + "events": "Événements",
703 + "reject": "Rejeter",
704 + "approve": "Approuver",
705 + "expiresOn": "Expire le",
706 + "walletConnect": "PortefeuilleConnect",
707 + "nullURIError": "L'URI est nul",
708 + "connectWalletPrompt": "Connectez votre portefeuille avec WalletConnect pour effectuer des transactions",
709 + "newConnection": "Nouvelle connexion",
710 + "activeConnectionsPrompt": "Les connexions actives apparaîtront ici",
711 + "deleteConnectionConfirmationPrompt": "Êtes-vous sûr de vouloir supprimer la connexion à",
712 + "event": "Événement",
713 + "successful": "Réussi",
714 + "wouoldLikeToConnect": "je voudrais me connecter",
715 + "message": "Message",
716 "do_not_have_enough_gas_asset": "Vous n'avez pas assez de ${currency} pour effectuer une transaction avec les conditions actuelles du réseau blockchain. Vous avez besoin de plus de ${currency} pour payer les frais du réseau blockchain, même si vous envoyez un actif différent.",
697 - "totp_auth_url": "URL D'AUTORISATION TOTP"
717 + "totp_auth_url": "URL D'AUTORISATION TOTP",
718 + "awaitDAppProcessing": "Veuillez attendre que le dApp termine le traitement."
719 }
res/values/strings_ha.arb
+22 -1
@@ -671,6 +671,27 @@
671 "default_buy_provider": "Tsohuwar Siyarwa",
672 "ask_each_time": "Tambaya kowane lokaci",
673 "buy_provider_unavailable": "Mai ba da kyauta a halin yanzu babu.",
674 + "signTransaction": "Sa hannu Ma'amala",
675 + "errorGettingCredentials": "Ba a yi nasara ba: Kuskure yayin samun takaddun shaida",
676 + "errorSigningTransaction": "An sami kuskure yayin sanya hannu kan ciniki",
677 + "pairingInvalidEvent": "Haɗa Lamarin mara inganci",
678 + "chains": "Sarkoki",
679 + "methods": "Hanyoyin",
680 + "events": "Abubuwan da suka faru",
681 + "reject": "Ƙi",
682 + "approve": "Amincewa",
683 + "expiresOn": "Yana ƙarewa",
684 + "walletConnect": "WalletConnect",
685 + "nullURIError": "URI banza ne",
686 + "connectWalletPrompt": "Haɗa walat ɗin ku tare da WalletConnect don yin ma'amala",
687 + "newConnection": "Sabuwar Haɗi",
688 + "activeConnectionsPrompt": "Haɗin kai mai aiki zai bayyana a nan",
689 + "deleteConnectionConfirmationPrompt": "Shin kun tabbata cewa kuna son share haɗin zuwa",
690 + "event": "Lamarin",
691 + "successful": "Nasara",
692 + "wouoldLikeToConnect": "ina son haɗi",
693 + "message": "Sako",
694 "do_not_have_enough_gas_asset": "Ba ku da isassun ${currency} don yin ma'amala tare da yanayin cibiyar sadarwar blockchain na yanzu. Kuna buƙatar ƙarin ${currency} don biyan kuɗaɗen cibiyar sadarwar blockchain, koda kuwa kuna aika wata kadara daban.",
675 - "totp_auth_url": "TOTP AUTH URL"
695 + "totp_auth_url": "TOTP AUTH URL",
696 + "awaitDAppProcessing": "Da fatan za a jira dApp ya gama aiki."
697 }
res/values/strings_hi.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "डिफ़ॉल्ट खरीद प्रदाता",
694 "ask_each_time": "हर बार पूछें",
695 "buy_provider_unavailable": "वर्तमान में प्रदाता अनुपलब्ध है।",
696 + "signTransaction": "लेन-देन पर हस्ताक्षर करें",
697 + "errorGettingCredentials": "विफल: क्रेडेंशियल प्राप्त करते समय त्रुटि",
698 + "errorSigningTransaction": "लेन-देन पर हस्ताक्षर करते समय एक त्रुटि उत्पन्न हुई है",
699 + "pairingInvalidEvent": "अमान्य ईवेंट युग्मित करना",
700 + "chains": "चेन",
701 + "methods": "तरीकों",
702 + "events": "आयोजन",
703 + "reject": "अस्वीकार करना",
704 + "approve": "मंज़ूरी देना",
705 + "expiresOn": "पर समय सीमा समाप्त",
706 + "walletConnect": "वॉलेटकनेक्ट",
707 + "nullURIError": "यूआरआई शून्य है",
708 + "connectWalletPrompt": "लेन-देन करने के लिए अपने वॉलेट को वॉलेटकनेक्ट से कनेक्ट करें",
709 + "newConnection": "नया कनेक्शन",
710 + "activeConnectionsPrompt": "सक्रिय कनेक्शन यहां दिखाई देंगे",
711 + "deleteConnectionConfirmationPrompt": "क्या आप वाकई कनेक्शन हटाना चाहते हैं?",
712 + "event": "आयोजन",
713 + "successful": "सफल",
714 + "wouoldLikeToConnect": "जुड़ना चाहेंगे",
715 + "message": "संदेश",
716 "do_not_have_enough_gas_asset": "वर्तमान ब्लॉकचेन नेटवर्क स्थितियों में लेनदेन करने के लिए आपके पास पर्याप्त ${currency} नहीं है। ब्लॉकचेन नेटवर्क शुल्क का भुगतान करने के लिए आपको अधिक ${currency} की आवश्यकता है, भले ही आप एक अलग संपत्ति भेज रहे हों।",
697 - "totp_auth_url": "TOTP प्रामाणिक यूआरएल"
717 + "totp_auth_url": "TOTP प्रामाणिक यूआरएल",
718 + "awaitDAppProcessing": "कृपया डीएपी की प्रोसेसिंग पूरी होने तक प्रतीक्षा करें।"
719 }
res/values/strings_hr.arb
+23 -2
@@ -693,6 +693,27 @@
693 "default_buy_provider": "Zadani davatelj kupnje",
694 "ask_each_time": "Pitajte svaki put",
695 "buy_provider_unavailable": "Davatelj trenutno nije dostupan.",
696 + "signTransaction": "Potpišite transakciju",
697 + "errorGettingCredentials": "Neuspješno: Pogreška prilikom dobivanja vjerodajnica",
698 + "errorSigningTransaction": "Došlo je do pogreške prilikom potpisivanja transakcije",
699 + "pairingInvalidEvent": "Nevažeći događaj uparivanja",
700 + "chains": "Lanci",
701 + "methods": "Metode",
702 + "events": "Događaji",
703 + "reject": "Odbiti",
704 + "approve": "Odobriti",
705 + "expiresOn": "Istječe",
706 + "walletConnect": "WalletConnect",
707 + "nullURIError": "URI je nula",
708 + "connectWalletPrompt": "Povežite svoj novčanik s WalletConnectom za obavljanje transakcija",
709 + "newConnection": "Nova veza",
710 + "activeConnectionsPrompt": "Ovdje će se pojaviti aktivne veze",
711 + "deleteConnectionConfirmationPrompt": "Jeste li sigurni da želite izbrisati vezu s",
712 + "event": "Događaj",
713 + "successful": "Uspješno",
714 + "wouoldLikeToConnect": "želio bi se povezati",
715 + "message": "Poruka",
716 "do_not_have_enough_gas_asset": "Nemate dovoljno ${currency} da izvršite transakciju s trenutačnim uvjetima blockchain mreže. Trebate više ${currency} da platite naknade za blockchain mrežu, čak i ako šaljete drugu imovinu.",
697 - "totp_auth_url": "TOTP AUTH URL"
698 -}
\ No newline at end of file
717 + "totp_auth_url": "TOTP AUTH URL",
718 + "awaitDAppProcessing": "Molimo pričekajte da dApp završi obradu."
719 +}
res/values/strings_id.arb
+23 -2
@@ -681,6 +681,27 @@
681 "default_buy_provider": "Penyedia beli default",
682 "ask_each_time": "Tanyakan setiap kali",
683 "buy_provider_unavailable": "Penyedia saat ini tidak tersedia.",
684 + "signTransaction": "Tandatangani Transaksi",
685 + "errorGettingCredentials": "Gagal: Terjadi kesalahan saat mendapatkan kredensial",
686 + "errorSigningTransaction": "Terjadi kesalahan saat menandatangani transaksi",
687 + "pairingInvalidEvent": "Menyandingkan Acara Tidak Valid",
688 + "chains": "Rantai",
689 + "methods": "Metode",
690 + "events": "Acara",
691 + "reject": "Menolak",
692 + "approve": "Menyetujui",
693 + "expiresOn": "Kadaluarsa pada",
694 + "walletConnect": "DompetConnect",
695 + "nullURIError": "URI adalah nol",
696 + "connectWalletPrompt": "Hubungkan dompet Anda dengan WalletConnect untuk melakukan transaksi",
697 + "newConnection": "Koneksi Baru",
698 + "activeConnectionsPrompt": "Koneksi aktif akan muncul di sini",
699 + "deleteConnectionConfirmationPrompt": "Apakah Anda yakin ingin menghapus koneksi ke",
700 + "event": "Peristiwa",
701 + "successful": "Berhasil",
702 + "wouoldLikeToConnect": "ingin terhubung",
703 + "message": "Pesan",
704 "do_not_have_enough_gas_asset": "Anda tidak memiliki cukup ${currency} untuk melakukan transaksi dengan kondisi jaringan blockchain saat ini. Anda memerlukan lebih banyak ${currency} untuk membayar biaya jaringan blockchain, meskipun Anda mengirimkan aset yang berbeda.",
685 - "totp_auth_url": "URL Otentikasi TOTP"
686 -}
\ No newline at end of file
705 + "totp_auth_url": "URL Otentikasi TOTP",
706 + "awaitDAppProcessing": "Mohon tunggu hingga dApp menyelesaikan pemrosesan."
707 +}
res/values/strings_it.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "Provider di acquisto predefinito",
694 "ask_each_time": "Chiedi ogni volta",
695 "buy_provider_unavailable": "Provider attualmente non disponibile.",
696 + "signTransaction": "Firma la transazione",
697 + "errorGettingCredentials": "Non riuscito: errore durante il recupero delle credenziali",
698 + "errorSigningTransaction": "Si è verificato un errore durante la firma della transazione",
699 + "pairingInvalidEvent": "Associazione evento non valido",
700 + "chains": "Catene",
701 + "methods": "Metodi",
702 + "events": "Eventi",
703 + "reject": "Rifiutare",
704 + "approve": "Approvare",
705 + "expiresOn": "Scade il",
706 + "walletConnect": "PortafoglioConnetti",
707 + "nullURIError": "L'URI è nullo",
708 + "connectWalletPrompt": "Collega il tuo portafoglio con WalletConnect per effettuare transazioni",
709 + "newConnection": "Nuova connessione",
710 + "activeConnectionsPrompt": "Le connessioni attive verranno visualizzate qui",
711 + "deleteConnectionConfirmationPrompt": "Sei sicuro di voler eliminare la connessione a",
712 + "event": "Evento",
713 + "successful": "Riuscito",
714 + "wouoldLikeToConnect": "vorrei connettermi",
715 + "message": "Messaggio",
716 "do_not_have_enough_gas_asset": "Non hai abbastanza ${currency} per effettuare una transazione con le attuali condizioni della rete blockchain. Hai bisogno di più ${currency} per pagare le commissioni della rete blockchain, anche se stai inviando una risorsa diversa.",
697 - "totp_auth_url": "URL DI AUT. TOTP"
717 + "totp_auth_url": "URL DI AUT. TOTP",
718 + "awaitDAppProcessing": "Attendi gentilmente che la dApp termini l'elaborazione."
719 }
res/values/strings_ja.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "デフォルトの購入プロバイダー",
694 "ask_each_time": "毎回尋ねてください",
695 "buy_provider_unavailable": "現在、プロバイダーは利用できません。",
696 + "signTransaction": "トランザクションに署名する",
697 + "errorGettingCredentials": "失敗: 認証情報の取得中にエラーが発生しました",
698 + "errorSigningTransaction": "トランザクションの署名中にエラーが発生しました",
699 + "pairingInvalidEvent": "ペアリング無効イベント",
700 + "chains": "チェーン",
701 + "methods": "メソッド",
702 + "events": "イベント",
703 + "reject": "拒否する",
704 + "approve": "承認する",
705 + "expiresOn": "有効期限は次のとおりです",
706 + "walletConnect": "ウォレットコネクト",
707 + "nullURIError": "URIがnullです",
708 + "connectWalletPrompt": "ウォレットを WalletConnect に接続して取引を行う",
709 + "newConnection": "新しい接続",
710 + "activeConnectionsPrompt": "アクティブな接続がここに表示されます",
711 + "deleteConnectionConfirmationPrompt": "への接続を削除してもよろしいですか?",
712 + "event": "イベント",
713 + "successful": "成功",
714 + "wouoldLikeToConnect": "接続したいです",
715 + "message": "メッセージ",
716 "do_not_have_enough_gas_asset": "現在のブロックチェーン ネットワークの状況では、トランザクションを行うのに十分な ${currency} がありません。別のアセットを送信する場合でも、ブロックチェーン ネットワーク料金を支払うにはさらに ${currency} が必要です。",
697 - "totp_auth_url": "TOTP認証URL"
717 + "totp_auth_url": "TOTP認証URL",
718 + "awaitDAppProcessing": "dAppの処理が完了するまでお待ちください。"
719 }
res/values/strings_ko.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "기본 구매 제공자",
694 "ask_each_time": "매번 물어보십시오",
695 "buy_provider_unavailable": "제공자는 현재 사용할 수 없습니다.",
696 + "signTransaction": "거래 서명",
697 + "errorGettingCredentials": "실패: 자격 증명을 가져오는 중 오류가 발생했습니다.",
698 + "errorSigningTransaction": "거래에 서명하는 동안 오류가 발생했습니다.",
699 + "pairingInvalidEvent": "잘못된 이벤트 페어링",
700 + "chains": "쇠사슬",
701 + "methods": "행동 양식",
702 + "events": "이벤트",
703 + "reject": "거부하다",
704 + "approve": "승인하다",
705 + "expiresOn": "만료 날짜",
706 + "walletConnect": "월렛커넥트",
707 + "nullURIError": "URI가 null입니다.",
708 + "connectWalletPrompt": "거래를 하려면 WalletConnect에 지갑을 연결하세요.",
709 + "newConnection": "새로운 연결",
710 + "activeConnectionsPrompt": "활성 연결이 여기에 표시됩니다",
711 + "deleteConnectionConfirmationPrompt": "다음 연결을 삭제하시겠습니까?",
712 + "event": "이벤트",
713 + "successful": "성공적인",
714 + "wouoldLikeToConnect": "연결하고 싶습니다",
715 + "message": "메시지",
716 "do_not_have_enough_gas_asset": "현재 블록체인 네트워크 조건으로 거래를 하기에는 ${currency}이(가) 충분하지 않습니다. 다른 자산을 보내더라도 블록체인 네트워크 수수료를 지불하려면 ${currency}가 더 필요합니다.",
697 - "totp_auth_url": "TOTP 인증 URL"
717 + "totp_auth_url": "TOTP 인증 URL",
718 + "awaitDAppProcessing": "dApp이 처리를 마칠 때까지 기다려주세요."
719 }
res/values/strings_my.arb
+22 -1
@@ -691,6 +691,27 @@
691 "default_buy_provider": "Default Provider ကိုဝယ်ပါ",
692 "ask_each_time": "တစ်ခုချင်းစီကိုအချိန်မေးပါ",
693 "buy_provider_unavailable": "လက်ရှိတွင်လက်ရှိမရနိုင်ပါ။",
694 + "signTransaction": "ငွေလွှဲဝင်ပါ။",
695 + "errorGettingCredentials": "မအောင်မြင်ပါ- အထောက်အထားများ ရယူနေစဉ် အမှားအယွင်း",
696 + "errorSigningTransaction": "ငွေပေးငွေယူ လက်မှတ်ထိုးစဉ် အမှားအယွင်းတစ်ခု ဖြစ်ပေါ်ခဲ့သည်။",
697 + "pairingInvalidEvent": "မမှန်ကန်သောဖြစ်ရပ်ကို တွဲချိတ်ခြင်း။",
698 + "chains": "ဆွဲကြိုး",
699 + "methods": "နည်းလမ်းများ",
700 + "events": "အဲ့ဒါနဲ့",
701 + "reject": "ငြင်းပယ်ပါ။",
702 + "approve": "လက်မခံပါ။",
703 + "expiresOn": "သက်တမ်းကုန်သည်။",
704 + "walletConnect": "Wallet ချိတ်ဆက်မှု",
705 + "nullURIError": "URI သည် null ဖြစ်သည်။",
706 + "connectWalletPrompt": "အရောင်းအဝယ်ပြုလုပ်ရန် သင့်ပိုက်ဆံအိတ်ကို WalletConnect နှင့် ချိတ်ဆက်ပါ။",
707 + "newConnection": "ချိတ်ဆက်မှုအသစ်",
708 + "activeConnectionsPrompt": "လက်ရှိချိတ်ဆက်မှုများ ဤနေရာတွင် ပေါ်လာပါမည်။",
709 + "deleteConnectionConfirmationPrompt": "ချိတ်ဆက်မှုကို ဖျက်လိုသည်မှာ သေချာပါသလား။",
710 + "event": "ပွဲ",
711 + "successful": "အောင်မြင်တယ်။",
712 + "wouoldLikeToConnect": "ချိတ်ဆက်ချင်ပါတယ်။",
713 + "message": "မက်ဆေ့ချ်",
714 "do_not_have_enough_gas_asset": "လက်ရှိ blockchain ကွန်ရက်အခြေအနေများနှင့် အရောင်းအဝယ်ပြုလုပ်ရန် သင့်တွင် ${currency} လုံလောက်မှုမရှိပါ။ သင်သည် မတူညီသော ပိုင်ဆိုင်မှုတစ်ခုကို ပေးပို့နေသော်လည်း blockchain ကွန်ရက်အခကြေးငွေကို ပေးဆောင်ရန် သင်သည် နောက်ထပ် ${currency} လိုအပ်ပါသည်။",
695 - "totp_auth_url": "TOTP AUTH URL"
715 + "totp_auth_url": "TOTP AUTH URL",
716 + "awaitDAppProcessing": "ကျေးဇူးပြု၍ dApp ကို စီမံလုပ်ဆောင်ခြင်း အပြီးသတ်ရန် စောင့်ပါ။"
717 }
res/values/strings_nl.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "Standaard Koopprovider",
694 "ask_each_time": "Vraag het elke keer",
695 "buy_provider_unavailable": "Provider momenteel niet beschikbaar.",
696 + "signTransaction": "Transactie ondertekenen",
697 + "errorGettingCredentials": "Mislukt: fout bij het ophalen van inloggegevens",
698 + "errorSigningTransaction": "Er is een fout opgetreden tijdens het ondertekenen van de transactie",
699 + "pairingInvalidEvent": "Koppelen Ongeldige gebeurtenis",
700 + "chains": "Ketens",
701 + "methods": "Methoden",
702 + "events": "Evenementen",
703 + "reject": "Afwijzen",
704 + "approve": "Goedkeuren",
705 + "expiresOn": "Verloopt op",
706 + "walletConnect": "WalletConnect",
707 + "nullURIError": "URI is nul",
708 + "connectWalletPrompt": "Verbind uw portemonnee met WalletConnect om transacties uit te voeren",
709 + "newConnection": "Nieuwe verbinding",
710 + "activeConnectionsPrompt": "Actieve verbindingen worden hier weergegeven",
711 + "deleteConnectionConfirmationPrompt": "Weet u zeker dat u de verbinding met",
712 + "event": "Evenement",
713 + "successful": "Succesvol",
714 + "wouoldLikeToConnect": "wil graag verbinden",
715 + "message": "Bericht",
716 "do_not_have_enough_gas_asset": "U heeft niet genoeg ${currency} om een transactie uit te voeren met de huidige blockchain-netwerkomstandigheden. U heeft meer ${currency} nodig om blockchain-netwerkkosten te betalen, zelfs als u een ander item verzendt.",
697 - "totp_auth_url": "TOTP AUTH-URL"
717 + "totp_auth_url": "TOTP AUTH-URL",
718 + "awaitDAppProcessing": "Wacht tot de dApp klaar is met verwerken."
719 }
res/values/strings_pl.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "Domyślny dostawca zakupu",
694 "ask_each_time": "Zapytaj za każdym razem",
695 "buy_provider_unavailable": "Dostawca obecnie niedostępny.",
696 + "signTransaction": "Podpisz transakcję",
697 + "errorGettingCredentials": "Niepowodzenie: Błąd podczas uzyskiwania poświadczeń",
698 + "errorSigningTransaction": "Wystąpił błąd podczas podpisywania transakcji",
699 + "pairingInvalidEvent": "Nieprawidłowe zdarzenie parowania",
700 + "chains": "Więzy",
701 + "methods": "Metody",
702 + "events": "Wydarzenia",
703 + "reject": "Odrzucić",
704 + "approve": "Zatwierdzić",
705 + "expiresOn": "Upływa w dniu",
706 + "walletConnect": "PortfelPołącz",
707 + "nullURIError": "URI ma wartość zerową",
708 + "connectWalletPrompt": "Połącz swój portfel z WalletConnect, aby dokonywać transakcji",
709 + "newConnection": "Nowe połączenie",
710 + "activeConnectionsPrompt": "Tutaj pojawią się aktywne połączenia",
711 + "deleteConnectionConfirmationPrompt": "Czy na pewno chcesz usunąć połączenie z",
712 + "event": "Wydarzenie",
713 + "successful": "Udany",
714 + "wouoldLikeToConnect": "chciałbym się połączyć",
715 + "message": "Wiadomość",
716 "do_not_have_enough_gas_asset": "Nie masz wystarczającej ilości ${currency}, aby dokonać transakcji przy bieżących warunkach sieci blockchain. Potrzebujesz więcej ${currency}, aby uiścić opłaty za sieć blockchain, nawet jeśli wysyłasz inny zasób.",
697 - "totp_auth_url": "Adres URL TOTP AUTH"
717 + "totp_auth_url": "Adres URL TOTP AUTH",
718 + "awaitDAppProcessing": "Poczekaj, aż dApp zakończy przetwarzanie."
719 }
res/values/strings_pt.arb
+22 -1
@@ -692,6 +692,27 @@
692 "default_buy_provider": "Provedor de compra padrão",
693 "ask_each_time": "Pergunte cada vez",
694 "buy_provider_unavailable": "Provedor atualmente indisponível.",
695 + "signTransaction": "Assinar transação",
696 + "errorGettingCredentials": "Falha: Erro ao obter credenciais",
697 + "errorSigningTransaction": "Ocorreu um erro ao assinar a transação",
698 + "pairingInvalidEvent": "Emparelhamento de evento inválido",
699 + "chains": "Correntes",
700 + "methods": "Métodos",
701 + "events": "Eventos",
702 + "reject": "Rejeitar",
703 + "approve": "Aprovar",
704 + "expiresOn": "Expira em",
705 + "walletConnect": "CarteiraConectada",
706 + "nullURIError": "URI é nulo",
707 + "connectWalletPrompt": "Conecte sua carteira ao WalletConnect para fazer transações",
708 + "newConnection": "Nova conexão",
709 + "activeConnectionsPrompt": "Conexões ativas aparecerão aqui",
710 + "deleteConnectionConfirmationPrompt": "Tem certeza de que deseja excluir a conexão com",
711 + "event": "Evento",
712 + "successful": "Bem-sucedido",
713 + "wouoldLikeToConnect": "gostaria de me conectar",
714 + "message": "Mensagem",
715 "do_not_have_enough_gas_asset": "Você não tem ${currency} suficiente para fazer uma transação com as condições atuais da rede blockchain. Você precisa de mais ${currency} para pagar as taxas da rede blockchain, mesmo se estiver enviando um ativo diferente.",
696 - "totp_auth_url": "URL de autenticação TOTP"
716 + "totp_auth_url": "URL de autenticação TOTP",
717 + "awaitDAppProcessing": "Aguarde até que o dApp termine o processamento."
718 }
res/values/strings_ru.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "По умолчанию поставщик покупки",
694 "ask_each_time": "Спросите каждый раз",
695 "buy_provider_unavailable": "Поставщик в настоящее время недоступен.",
696 + "signTransaction": "Подписать транзакцию",
697 + "errorGettingCredentials": "Не удалось: ошибка при получении учетных данных.",
698 + "errorSigningTransaction": "Произошла ошибка при подписании транзакции",
699 + "pairingInvalidEvent": "Недействительное событие сопряжения",
700 + "chains": "Цепи",
701 + "methods": "Методы",
702 + "events": "События",
703 + "reject": "Отклонять",
704 + "approve": "Утвердить",
705 + "expiresOn": "Годен до",
706 + "walletConnect": "КошелекПодключиться",
707 + "nullURIError": "URI имеет значение null",
708 + "connectWalletPrompt": "Подключите свой кошелек к WalletConnect для совершения транзакций.",
709 + "newConnection": "Новое соединение",
710 + "activeConnectionsPrompt": "Здесь появятся активные подключения",
711 + "deleteConnectionConfirmationPrompt": "Вы уверены, что хотите удалить подключение к",
712 + "event": "Событие",
713 + "successful": "Успешный",
714 + "wouoldLikeToConnect": "хотел бы подключиться",
715 + "message": "Сообщение",
716 "do_not_have_enough_gas_asset": "У вас недостаточно ${currency} для совершения транзакции при текущих условиях сети блокчейн. Вам нужно больше ${currency} для оплаты комиссий за сеть блокчейна, даже если вы отправляете другой актив.",
697 - "totp_auth_url": "URL-адрес TOTP-АВТОРИЗАЦИИ"
717 + "totp_auth_url": "URL-адрес TOTP-АВТОРИЗАЦИИ",
718 + "awaitDAppProcessing": "Пожалуйста, подождите, пока dApp завершит обработку."
719 }
res/values/strings_th.arb
+22 -1
@@ -691,6 +691,27 @@
691 "default_buy_provider": "ผู้ให้บริการซื้อเริ่มต้น",
692 "ask_each_time": "ถามทุกครั้ง",
693 "buy_provider_unavailable": "ผู้ให้บริการไม่สามารถใช้งานได้ในปัจจุบัน",
694 + "signTransaction": "ลงนามในการทำธุรกรรม",
695 + "errorGettingCredentials": "ล้มเหลว: เกิดข้อผิดพลาดขณะรับข้อมูลรับรอง",
696 + "errorSigningTransaction": "เกิดข้อผิดพลาดขณะลงนามธุรกรรม",
697 + "pairingInvalidEvent": "การจับคู่เหตุการณ์ที่ไม่ถูกต้อง",
698 + "chains": "ห่วงโซ่",
699 + "methods": "วิธีการ",
700 + "events": "กิจกรรม",
701 + "reject": "ปฏิเสธ",
702 + "approve": "อนุมัติ",
703 + "expiresOn": "หมดอายุวันที่",
704 + "walletConnect": "WalletConnect",
705 + "nullURIError": "URI เป็นโมฆะ",
706 + "connectWalletPrompt": "เชื่อมต่อกระเป๋าเงินของคุณด้วย WalletConnect เพื่อทำธุรกรรม",
707 + "newConnection": "การเชื่อมต่อใหม่",
708 + "activeConnectionsPrompt": "การเชื่อมต่อที่ใช้งานอยู่จะปรากฏที่นี่",
709 + "deleteConnectionConfirmationPrompt": "คุณแน่ใจหรือไม่ว่าต้องการลบการเชื่อมต่อไปยัง",
710 + "event": "เหตุการณ์",
711 + "successful": "ประสบความสำเร็จ",
712 + "wouoldLikeToConnect": "ต้องการเชื่อมต่อ",
713 + "message": "ข้อความ",
714 "do_not_have_enough_gas_asset": "คุณมี ${currency} ไม่เพียงพอที่จะทำธุรกรรมกับเงื่อนไขเครือข่ายบล็อคเชนในปัจจุบัน คุณต้องมี ${currency} เพิ่มขึ้นเพื่อชำระค่าธรรมเนียมเครือข่ายบล็อคเชน แม้ว่าคุณจะส่งสินทรัพย์อื่นก็ตาม",
695 - "totp_auth_url": "URL การตรวจสอบสิทธิ์ TOTP"
715 + "totp_auth_url": "URL การตรวจสอบสิทธิ์ TOTP",
716 + "awaitDAppProcessing": "โปรดรอให้ dApp ประมวลผลเสร็จสิ้น"
717 }
res/values/strings_tl.arb
+29 -2
@@ -688,6 +688,33 @@
688 "support_description_other_links": "Sumali sa aming mga komunidad o maabot sa amin ang aming mga kasosyo sa pamamagitan ng iba pang mga pamamaraan",
689 "select_destination": "Mangyaring piliin ang patutunguhan para sa backup file.",
690 "save_to_downloads": "I -save sa mga pag -download",
691 + "select_buy_provider_notice": "Pumili ng provider ng pagbili sa itaas. Maaari mong laktawan ang screen na ito sa pamamagitan ng pagtatakda ng iyong default na provider ng pagbili sa mga setting ng app.",
692 + "onramper_option_description": "Mabilis na bumili ng crypto na may maraming paraan ng pagbabayad. Available sa karamihan ng mga bansa. Iba-iba ang mga spread at bayarin.",
693 + "default_buy_provider": "Default na Provider ng Pagbili",
694 + "ask_each_time": "Magtanong sa bawat oras",
695 + "robinhood_option_description": "Bumili at ilipat kaagad gamit ang iyong debit card, bank account, o balanse ng Robinhood. USA lang.",
696 + "buy_provider_unavailable": "Kasalukuyang hindi available ang provider.",
697 + "signTransaction": "Mag-sign Transaksyon",
698 + "errorGettingCredentials": "Nabigo: Error habang kumukuha ng mga kredensyal",
699 + "errorSigningTransaction": "May naganap na error habang pinipirmahan ang transaksyon",
700 + "pairingInvalidEvent": "Pagpares ng Di-wastong Kaganapan",
701 + "chains": "Mga tanikala",
702 + "methods": "Paraan",
703 + "events": "Mga kaganapan",
704 + "reject": "Tanggihan",
705 + "approve": "Aprubahan",
706 + "expiresOn": "Mag-e-expire sa",
707 + "walletConnect": "WalletConnect",
708 + "nullURIError": "Ang URI ay null",
709 + "connectWalletPrompt": "Ikonekta ang iyong wallet sa WalletConnect upang gumawa ng mga transaksyon",
710 + "newConnection": "Bagong Koneksyon",
711 + "activeConnectionsPrompt": "Lalabas dito ang mga aktibong koneksyon",
712 + "deleteConnectionConfirmationPrompt": "Sigurado ka bang gusto mong tanggalin ang koneksyon sa",
713 + "event": "Kaganapan",
714 + "successful": "Matagumpay",
715 + "wouoldLikeToConnect": "gustong kumonekta",
716 + "message": "Mensahe",
717 "do_not_have_enough_gas_asset": "Wala kang sapat na ${currency} para gumawa ng transaksyon sa kasalukuyang kundisyon ng network ng blockchain. Kailangan mo ng higit pang ${currency} upang magbayad ng mga bayarin sa network ng blockchain, kahit na nagpapadala ka ng ibang asset.",
692 - "totp_auth_url": "TOTP AUTH URL"
693 -}
\ No newline at end of file
718 + "totp_auth_url": "TOTP AUTH URL",
719 + "awaitDAppProcessing": "Pakihintay na matapos ang pagproseso ng dApp."
720 +}
res/values/strings_tr.arb
+22 -1
@@ -691,6 +691,27 @@
691 "default_buy_provider": "Varsayılan Satın Alma Sağlayıcısı",
692 "ask_each_time": "Her seferinde sor",
693 "buy_provider_unavailable": "Sağlayıcı şu anda kullanılamıyor.",
694 + "signTransaction": "İşlem İmzala",
695 + "errorGettingCredentials": "Başarısız: Kimlik bilgileri alınırken hata oluştu",
696 + "errorSigningTransaction": "İşlem imzalanırken bir hata oluştu",
697 + "pairingInvalidEvent": "Geçersiz Etkinliği Eşleştirme",
698 + "chains": "Zincirler",
699 + "methods": "Yöntemler",
700 + "events": "Olaylar",
701 + "reject": "Reddetmek",
702 + "approve": "Onaylamak",
703 + "expiresOn": "Tarihinde sona eriyor",
704 + "walletConnect": "WalletConnect",
705 + "nullURIError": "URI boş",
706 + "connectWalletPrompt": "İşlem yapmak için cüzdanınızı WalletConnect'e bağlayın",
707 + "newConnection": "Yeni bağlantı",
708 + "activeConnectionsPrompt": "Aktif bağlantılar burada görünecek",
709 + "deleteConnectionConfirmationPrompt": "Bağlantıyı silmek istediğinizden emin misiniz?",
710 + "event": "Etkinlik",
711 + "successful": "Başarılı",
712 + "wouoldLikeToConnect": "bağlanmak istiyorum",
713 + "message": "İleti",
714 "do_not_have_enough_gas_asset": "Mevcut blockchain ağ koşullarıyla işlem yapmak için yeterli ${currency} paranız yok. Farklı bir varlık gönderiyor olsanız bile blockchain ağ ücretlerini ödemek için daha fazla ${currency} miktarına ihtiyacınız var.",
695 - "totp_auth_url": "TOTP YETKİ URL'si"
715 + "totp_auth_url": "TOTP YETKİ URL'si",
716 + "awaitDAppProcessing": "Lütfen dApp'in işlemeyi bitirmesini bekleyin."
717 }
res/values/strings_uk.arb
+22 -1
@@ -693,6 +693,27 @@
693 "default_buy_provider": "Постачальник покупки за замовчуванням",
694 "ask_each_time": "Запитайте кожен раз",
695 "buy_provider_unavailable": "В даний час постачальник недоступний.",
696 + "signTransaction": "Підписати транзакцію",
697 + "errorGettingCredentials": "Помилка: помилка під час отримання облікових даних",
698 + "errorSigningTransaction": "Під час підписання транзакції сталася помилка",
699 + "pairingInvalidEvent": "Недійсна подія сполучення",
700 + "chains": "Ланцюги",
701 + "methods": "методи",
702 + "events": "Події",
703 + "reject": "Відхиляти",
704 + "approve": "Затвердити",
705 + "expiresOn": "Термін дії закінчується",
706 + "walletConnect": "WalletConnect",
707 + "nullURIError": "URI нульовий",
708 + "connectWalletPrompt": "Підключіть свій гаманець до WalletConnect, щоб здійснювати транзакції",
709 + "newConnection": "Нове підключення",
710 + "activeConnectionsPrompt": "Тут з’являться активні підключення",
711 + "deleteConnectionConfirmationPrompt": "Ви впевнені, що хочете видалити з’єднання з",
712 + "event": "Подія",
713 + "successful": "Успішний",
714 + "wouoldLikeToConnect": "хотів би підключитися",
715 + "message": "повідомлення",
716 "do_not_have_enough_gas_asset": "У вас недостатньо ${currency}, щоб здійснити трансакцію з поточними умовами мережі блокчейн. Вам потрібно більше ${currency}, щоб сплатити комісію мережі блокчейн, навіть якщо ви надсилаєте інший актив.",
697 - "totp_auth_url": "TOTP AUTH URL"
717 + "totp_auth_url": "TOTP AUTH URL",
718 + "awaitDAppProcessing": "Зачекайте, доки dApp завершить обробку."
719 }
res/values/strings_ur.arb
+22 -1
@@ -685,6 +685,27 @@
685 "default_buy_provider": "پہلے سے طے شدہ خریدنے والا",
686 "ask_each_time": "ہر بار پوچھیں",
687 "buy_provider_unavailable": "فراہم کنندہ فی الحال دستیاب نہیں ہے۔",
688 + "signTransaction": "۔ﮟﯾﺮﮐ ﻂﺨﺘﺳﺩ ﺮﭘ ﻦﯾﺩ ﻦﯿﻟ",
689 + "errorGettingCredentials": "۔ﯽﺑﺍﺮﺧ ﮟﯿﻣ ﮯﻧﺮﮐ ﻞﺻﺎﺣ ﺩﺎﻨﺳﺍ :ﻡﺎﮐﺎﻧ",
690 + "errorSigningTransaction": "۔ﮯﮨ ﯽﺌﮔﺁ ﺶﯿﭘ ﯽﺑﺍﺮﺧ ﮏﯾﺍ ﺖﻗﻭ ﮯﺗﺮﮐ ﻂﺨﺘﺳﺩ ﺮﭘ ﻦﯾﺩ ﻦﯿﻟ",
691 + "pairingInvalidEvent": "ﭧﻧﻮﯾﺍ ﻂﻠﻏ ﺎﻧﺎﻨﺑ ﺍﮌﻮﺟ",
692 + "chains": "ﮟﯾﺮﯿﺠﻧﺯ",
693 + "methods": "ﮯﻘﯾﺮﻃ",
694 + "events": "ﺕﺎﺒﯾﺮﻘﺗ",
695 + "reject": "ﺎﻧﺮﮐ ﺩﺭ",
696 + "approve": "ﻭﺮﮐ ﺭﻮﻈﻨﻣ",
697 + "expiresOn": "ﺩﺎﻌﯿﻣ ﯽﻣﺎﺘﺘﺧﺍ",
698 + "walletConnect": "WalletConnect",
699 + "nullURIError": "URI ۔ﮯﮨ ﻡﺪﻌﻟﺎﮐ",
700 + "connectWalletPrompt": "۔ﮟﯾﮌﻮﺟ ﮫﺗﺎﺳ ﮯﮐ WalletConnect ﻮﮐ ﮮﻮﭩﺑ ﮯﻨﭘﺍ ﮯﯿﻟ ﮯﮐ ﮯﻧﺮﮐ ﻦﯾﺩ ﻦﯿﻟ",
701 + "newConnection": "ﻦﺸﮑﻨﮐ ﺎﯿﻧ",
702 + "activeConnectionsPrompt": "۔ﮯﮔ ﮞﻮﮨ ﺮﮨﺎﻇ ﮞﺎﮩﯾ ﺰﻨﺸﮑﻨﮐ ﻝﺎﻌﻓ",
703 + "deleteConnectionConfirmationPrompt": "۔ﮟﯿﮨ ﮯﺘﮨﺎﭼ ﺎﻧﺮﮐ ﻑﺬﺣ ﻮﮐ ﻦﺸﮑﻨﮐ ﭖﺁ ﮧﮐ ﮯﮨ ﻦﯿﻘﯾ ﻮﮐ ﭖﺁ ﺎﯿﮐ",
704 + "event": "ﺐﯾﺮﻘﺗ",
705 + "successful": "ﺏﺎﯿﻣﺎﮐ",
706 + "wouoldLikeToConnect": "؟ﮯﮔ ﮟﯿﮨﺎﭼ ﺎﻧﮍﺟ",
707 + "message": "ﻡﺎﻐﯿﭘ",
708 "do_not_have_enough_gas_asset": "آپ کے پاس موجودہ بلاکچین نیٹ ورک کی شرائط کے ساتھ لین دین کرنے کے لیے کافی ${currency} نہیں ہے۔ آپ کو بلاکچین نیٹ ورک کی فیس ادا کرنے کے لیے مزید ${currency} کی ضرورت ہے، چاہے آپ کوئی مختلف اثاثہ بھیج رہے ہوں۔",
689 - "totp_auth_url": "TOTP AUTH URL"
709 + "totp_auth_url": "TOTP AUTH URL",
710 + "awaitDAppProcessing": "۔ﮟﯾﺮﮐ ﺭﺎﻈﺘﻧﺍ ﺎﮐ ﮯﻧﻮﮨ ﻞﻤﮑﻣ ﮓﻨﺴﯿﺳﻭﺮﭘ ﮯﮐ dApp ﻡﺮﮐ ﮦﺍﺮﺑ"
711 }
res/values/strings_yo.arb
+22 -1
@@ -687,6 +687,27 @@
687 "default_buy_provider": "Aiyipada Ra Olupese",
688 "ask_each_time": "Beere lọwọ kọọkan",
689 "buy_provider_unavailable": "Olupese lọwọlọwọ ko si.",
690 + "signTransaction": "Wole Idunadura",
691 + "errorGettingCredentials": "Kuna: Aṣiṣe lakoko gbigba awọn iwe-ẹri",
692 + "errorSigningTransaction": "Aṣiṣe kan ti waye lakoko ti o fowo si iṣowo",
693 + "pairingInvalidEvent": "Pipọpọ Iṣẹlẹ Ti ko tọ",
694 + "chains": "Awọn ẹwọn",
695 + "methods": "Awọn ọna",
696 + "events": "Awọn iṣẹlẹ",
697 + "reject": "Kọ",
698 + "approve": "Fi ọwọ si",
699 + "expiresOn": "Ipari lori",
700 + "walletConnect": "Asopọmọra apamọwọ",
701 + "nullURIError": "URI jẹ asan",
702 + "connectWalletPrompt": "So apamọwọ rẹ pọ pẹlu WalletConnect lati ṣe awọn iṣowo",
703 + "newConnection": "Tuntun Asopọ",
704 + "activeConnectionsPrompt": "Awọn asopọ ti nṣiṣe lọwọ yoo han nibi",
705 + "deleteConnectionConfirmationPrompt": "Ṣe o da ọ loju pe o fẹ paarẹ asopọ si",
706 + "event": "Iṣẹlẹ",
707 + "successful": "Aseyori",
708 + "wouoldLikeToConnect": "yoo fẹ lati sopọ",
709 + "message": "Ifiranṣẹ",
710 "do_not_have_enough_gas_asset": "O ko ni to ${currency} lati ṣe idunadura kan pẹlu awọn ipo nẹtiwọki blockchain lọwọlọwọ. O nilo diẹ sii ${currency} lati san awọn owo nẹtiwọọki blockchain, paapaa ti o ba nfi dukia miiran ranṣẹ.",
691 - "totp_auth_url": "TOTP AUTH URL"
711 + "totp_auth_url": "TOTP AUTH URL",
712 + "awaitDAppProcessing": "Fi inurere duro fun dApp lati pari sisẹ."
713 }
res/values/strings_zh.arb
+22 -1
@@ -692,6 +692,27 @@
692 "default_buy_provider": "默认购买提供商",
693 "ask_each_time": "每次问",
694 "buy_provider_unavailable": "提供者目前不可用。",
695 + "signTransaction": "签署交易",
696 + "errorGettingCredentials": "失败:获取凭据时出错",
697 + "errorSigningTransaction": "签署交易时发生错误",
698 + "pairingInvalidEvent": "配对无效事件",
699 + "chains": "链条",
700 + "methods": "方法",
701 + "events": "活动",
702 + "reject": "拒绝",
703 + "approve": "批准",
704 + "expiresOn": "到期",
705 + "walletConnect": "钱包连接",
706 + "nullURIError": "URI 为空",
707 + "connectWalletPrompt": "将您的钱包与 WalletConnect 连接以进行交易",
708 + "newConnection": "新连接",
709 + "activeConnectionsPrompt": "活动连接将出现在这里",
710 + "deleteConnectionConfirmationPrompt": "您确定要删除与",
711 + "event": "事件",
712 + "successful": "成功的",
713 + "wouoldLikeToConnect": "想要连接",
714 + "message": "信息",
715 "do_not_have_enough_gas_asset": "您没有足够的 ${currency} 来在当前的区块链网络条件下进行交易。即使您发送的是不同的资产,您也需要更多的 ${currency} 来支付区块链网络费用。",
696 - "totp_auth_url": "TOTP 授权 URL"
716 + "totp_auth_url": "TOTP 授权 URL",
717 + "awaitDAppProcessing": "请等待 dApp 处理完成。"
718 }
tool/configure.dart
+3
@@ -477,6 +477,7 @@ import 'package:cw_core/wallet_base.dart';
477 import 'package:cw_core/wallet_credentials.dart';
478 import 'package:cw_core/wallet_info.dart';
479 import 'package:cw_core/wallet_service.dart';
480 +import 'package:eth_sig_util/util/utils.dart';
481 import 'package:hive/hive.dart';
482 """;
483 const ethereumCWHeaders = """
@@ -498,6 +499,8 @@ abstract class Ethereum {
499 WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password});
500 WalletCredentials createEthereumRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password});
501 String getAddress(WalletBase wallet);
502 + String getPrivateKey(WalletBase wallet);
503 + String getPublicKey(WalletBase wallet);
504 TransactionPriority getDefaultTransactionPriority();
505 List<TransactionPriority> getTransactionPriorities();
506 TransactionPriority deserializeEthereumTransactionPriority(int raw);
tool/utils/secret_key.dart
+1
@@ -35,6 +35,7 @@ class SecretKey {
35 SecretKey('exolixApiKey', () => ''),
36 SecretKey('robinhoodApplicationId', () => ''),
37 SecretKey('robinhoodCIdApiSecret', () => ''),
38 + SecretKey('walletConnectProjectId', () => ''),
39 ];
40
41 static final ethereumSecrets = [