CW-1069-implement-deuro-dapp-support (#2304)

* feat: started dEuro Savings integration * fix: merge conflict regarding theming * feat: Add dEuro Savings Screen * feat: Change DEuro Savings UI * feat: Complete DEuro Savings integration with UI enhancements and transaction support * style: remove forgotten print statements * feat: localize dEuro subtitle * feat: add approval flow and priority handling to DEuro Savings integration - Introduced approval flow for DEuro Savings to enable token approvals. - Added priority handling for deposit and withdrawal operations. - Updated UI to support approval state and interactions. - Localized new strings for multiple languages. - Enhanced transaction handling with separate approval and commit actions. * feat: add support for ERC20 token approval transactions - Introduced `signApprovalTransaction` and `createApprovalTransaction` methods. - Added handling for infinite approvals. - Implemented encoding for approval transaction data. - Enhanced transaction creation flow with approval-specific functionality. * Update UI * feat: enhance DEuro Savings logic and UI with computed property and fix gradient background * feat: localize transaction confirmation content for DEuro Savings * feat: enable interest collection for DEuro Savings with localized support * fix reformatting [skip ci] --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza> Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>

Konstantin Ullrich committed Jun 19, 2025 at 04:37 UTC edaf48599350adf7c0c00589801ab842ac0bbffa
49 files changed +2081 -66
cw_ethereum/lib/deuro/deuro_savings.dart new
+121
@@ -0,0 +1,121 @@
1 +import 'dart:convert';
2 +import 'dart:typed_data';
3 +
4 +import 'package:crypto/crypto.dart';
5 +import 'package:cw_core/crypto_currency.dart';
6 +import 'package:cw_ethereum/deuro/deuro_savings_contract.dart';
7 +import 'package:cw_ethereum/ethereum_wallet.dart';
8 +import 'package:cw_evm/contract/erc20.dart';
9 +import 'package:cw_evm/evm_chain_transaction_priority.dart';
10 +import 'package:cw_evm/pending_evm_chain_transaction.dart';
11 +import 'package:web3dart/web3dart.dart';
12 +
13 +const String savingsGatewayAddress =
14 + "0x073493d73258C4BEb6542e8dd3e1b2891C972303";
15 +
16 +const String dEuroAddress = "0xbA3f535bbCcCcA2A154b573Ca6c5A49BAAE0a3ea";
17 +
18 +class DEuro {
19 + final SavingsGateway _savingsGateway;
20 + final ERC20 _dEuro;
21 + final EthereumWallet _wallet;
22 +
23 + DEuro(EthereumWallet wallet)
24 + : _wallet = wallet,
25 + _savingsGateway = _getSavingsGateway(wallet.getWeb3Client()!),
26 + _dEuro = _getDEuroToken(wallet.getWeb3Client()!);
27 +
28 + static SavingsGateway _getSavingsGateway(Web3Client client) => SavingsGateway(
29 + address: EthereumAddress.fromHex(savingsGatewayAddress),
30 + client: client,
31 + );
32 +
33 + static ERC20 _getDEuroToken(Web3Client client) => ERC20(
34 + address: EthereumAddress.fromHex(dEuroAddress),
35 + client: client,
36 + );
37 +
38 + final frontendCode =
39 + Uint8List.fromList(sha256.convert(utf8.encode("wallet")).bytes);
40 +
41 + EthereumAddress get _address =>
42 + EthereumAddress.fromHex(_wallet.walletAddresses.primaryAddress);
43 +
44 + Future<BigInt> get savingsBalance async =>
45 + (await _savingsGateway.savings(accountOwner: _address)).saved;
46 +
47 + Future<BigInt> get accruedInterest =>
48 + _savingsGateway.accruedInterest(accountOwner: _address);
49 +
50 + Future<BigInt> get interestRate => _savingsGateway.currentRatePPM();
51 +
52 + Future<BigInt> get approvedBalance =>
53 + _dEuro.allowance(_address, _savingsGateway.self.address);
54 +
55 + Future<PendingEVMChainTransaction> depositSavings(
56 + BigInt amount, EVMChainTransactionPriority priority) async {
57 + final signedTransaction = await _savingsGateway.save(
58 + (amount: amount, frontendCode: frontendCode),
59 + credentials: _wallet.evmChainPrivateKey,
60 + );
61 +
62 + final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
63 + amount: amount,
64 + contractAddress: _savingsGateway.self.address.hexEip55,
65 + receivingAddressHex: _savingsGateway.self.address.hexEip55,
66 + priority: priority,
67 + data: _savingsGateway.self.abi.functions[17]
68 + .encodeCall([amount, frontendCode]),
69 + );
70 +
71 + final sendTransaction =
72 + () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
73 +
74 + return PendingEVMChainTransaction(
75 + sendTransaction: sendTransaction,
76 + signedTransaction: signedTransaction,
77 + fee: BigInt.from(fee.estimatedGasFee),
78 + amount: amount.toString(),
79 + exponent: 18);
80 + }
81 +
82 + Future<PendingEVMChainTransaction> withdrawSavings(
83 + BigInt amount, EVMChainTransactionPriority priority) async {
84 + final signedTransaction = await _savingsGateway.withdraw(
85 + (target: _address, amount: amount, frontendCode: frontendCode),
86 + credentials: _wallet.evmChainPrivateKey,
87 + );
88 +
89 + final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
90 + amount: amount,
91 + contractAddress: _savingsGateway.self.address.hexEip55,
92 + receivingAddressHex: _savingsGateway.self.address.hexEip55,
93 + priority: priority,
94 + data: _savingsGateway.self.abi.functions[17]
95 + .encodeCall([amount, frontendCode]),
96 + );
97 +
98 + final sendTransaction =
99 + () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
100 +
101 + return PendingEVMChainTransaction(
102 + sendTransaction: sendTransaction,
103 + signedTransaction: signedTransaction,
104 + fee: BigInt.from(fee.estimatedGasFee),
105 + amount: amount.toString(),
106 + exponent: 18);
107 + }
108 +
109 + // Set an infinite approval to save gas in the future
110 + Future<PendingEVMChainTransaction> enableSavings(
111 + EVMChainTransactionPriority priority) async =>
112 + (await _wallet.createApprovalTransaction(
113 + BigInt.parse(
114 + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
115 + radix: 16,
116 + ),
117 + _savingsGateway.self.address.hexEip55,
118 + CryptoCurrency.deuro,
119 + priority,
120 + )) as PendingEVMChainTransaction;
121 +}
cw_ethereum/lib/deuro/deuro_savings_contract.dart new
+543
@@ -0,0 +1,543 @@
1 +// ignore_for_file: type=lint
2 +// ignore_for_file: unused_local_variable, unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
3 +// ignore_for_file: no_leading_underscores_for_library_prefixes
4 +import 'package:web3dart/web3dart.dart' as _i1;
5 +import 'dart:typed_data' as _i2;
6 +
7 +final _contractAbi = _i1.ContractAbi.fromJson(
8 + '[{"inputs":[{"internalType":"contract IDecentralizedEURO","name":"deuro_","type":"address"},{"internalType":"uint24","name":"initialRatePPM","type":"uint24"},{"internalType":"address","name":"gateway_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ChangeNotReady","type":"error"},{"inputs":[],"name":"ModuleDisabled","type":"error"},{"inputs":[],"name":"NoPendingChange","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"interest","type":"uint256"}],"name":"InterestCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint24","name":"newRate","type":"uint24"}],"name":"RateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint24","name":"nextRate","type":"uint24"},{"indexed":false,"internalType":"uint40","name":"nextChange","type":"uint40"}],"name":"RateProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint192","name":"amount","type":"uint192"}],"name":"Saved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint192","name":"amount","type":"uint192"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"GATEWAY","outputs":[{"internalType":"contract IFrontendGateway","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountOwner","type":"address"}],"name":"accruedInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountOwner","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"accruedInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint192","name":"targetAmount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"targetAmount","type":"uint192"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"applyChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint192","name":"saved","type":"uint192"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"internalType":"struct Savings.Account","name":"account","type":"tuple"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"name":"calculateInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRatePPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTicks","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deuro","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"equity","outputs":[{"internalType":"contract IReserve","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextChange","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextRatePPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"newRatePPM_","type":"uint24"},{"internalType":"address[]","name":"helpers","type":"address[]"}],"name":"proposeChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"refreshBalance","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refreshMyBalance","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"amount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"savings","outputs":[{"internalType":"uint192","name":"saved","type":"uint192"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"ticks","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"},{"internalType":"bytes32","name":"frontendCode","type":"bytes32"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]',
9 + 'SavingsGateway',
10 +);
11 +
12 +class SavingsGateway extends _i1.GeneratedContract {
13 + SavingsGateway({
14 + required _i1.EthereumAddress address,
15 + required _i1.Web3Client client,
16 + int? chainId,
17 + }) : super(
18 + _i1.DeployedContract(
19 + _contractAbi,
20 + address,
21 + ),
22 + client,
23 + chainId,
24 + );
25 +
26 + /// The optional [atBlock] parameter can be used to view historical data. When
27 + /// set, the function will be evaluated in the specified block. By default, the
28 + /// latest on-chain block will be used.
29 + Future<_i1.EthereumAddress> GATEWAY({_i1.BlockNum? atBlock}) async {
30 + final function = self.abi.functions[1];
31 + assert(checkSignature(function, '338c5371'));
32 + final params = [];
33 + final response = await read(
34 + function,
35 + params,
36 + atBlock,
37 + );
38 + return (response[0] as _i1.EthereumAddress);
39 + }
40 +
41 + /// The optional [atBlock] parameter can be used to view historical data. When
42 + /// set, the function will be evaluated in the specified block. By default, the
43 + /// latest on-chain block will be used.
44 + Future<BigInt> accruedInterest({
45 + required _i1.EthereumAddress accountOwner,
46 + _i1.BlockNum? atBlock,
47 + }) async {
48 + final function = self.abi.functions[2];
49 + assert(checkSignature(function, '77267ec3'));
50 + final params = [accountOwner];
51 + final response = await read(
52 + function,
53 + params,
54 + atBlock,
55 + );
56 + return (response[0] as BigInt);
57 + }
58 +
59 + /// The optional [atBlock] parameter can be used to view historical data. When
60 + /// set, the function will be evaluated in the specified block. By default, the
61 + /// latest on-chain block will be used.
62 + Future<BigInt> accruedInterest$2(
63 + ({_i1.EthereumAddress accountOwner, BigInt timestamp}) args, {
64 + _i1.BlockNum? atBlock,
65 + }) async {
66 + final function = self.abi.functions[3];
67 + assert(checkSignature(function, 'a696399d'));
68 + final params = [
69 + args.accountOwner,
70 + args.timestamp,
71 + ];
72 + final response = await read(
73 + function,
74 + params,
75 + atBlock,
76 + );
77 + return (response[0] as BigInt);
78 + }
79 +
80 + /// The optional [transaction] parameter can be used to override parameters
81 + /// like the gas price, nonce and max gas. The `data` and `to` fields will be
82 + /// set by the contract.
83 + Future<String> adjust(
84 + ({BigInt targetAmount, _i2.Uint8List frontendCode}) args, {
85 + required _i1.Credentials credentials,
86 + _i1.Transaction? transaction,
87 + }) async {
88 + final function = self.abi.functions[4];
89 + assert(checkSignature(function, '753ef93c'));
90 + final params = [
91 + args.targetAmount,
92 + args.frontendCode,
93 + ];
94 + return write(
95 + credentials,
96 + transaction,
97 + function,
98 + params,
99 + );
100 + }
101 +
102 + /// The optional [atBlock] parameter can be used to view historical data. When
103 + /// set, the function will be evaluated in the specified block. By default, the
104 + /// latest on-chain block will be used.
105 + Future<BigInt> calculateInterest(
106 + ({dynamic account, BigInt ticks}) args, {
107 + _i1.BlockNum? atBlock,
108 + }) async {
109 + final function = self.abi.functions[7];
110 + assert(checkSignature(function, '7915ce20'));
111 + final params = [
112 + args.account,
113 + args.ticks,
114 + ];
115 + final response = await read(
116 + function,
117 + params,
118 + atBlock,
119 + );
120 + return (response[0] as BigInt);
121 + }
122 +
123 + /// The optional [atBlock] parameter can be used to view historical data. When
124 + /// set, the function will be evaluated in the specified block. By default, the
125 + /// latest on-chain block will be used.
126 + Future<BigInt> currentRatePPM({_i1.BlockNum? atBlock}) async {
127 + final function = self.abi.functions[8];
128 + assert(checkSignature(function, '06a7b376'));
129 + final params = [];
130 + final response = await read(
131 + function,
132 + params,
133 + atBlock,
134 + );
135 + return (response[0] as BigInt);
136 + }
137 +
138 + /// The optional [atBlock] parameter can be used to view historical data. When
139 + /// set, the function will be evaluated in the specified block. By default, the
140 + /// latest on-chain block will be used.
141 + Future<BigInt> currentTicks({_i1.BlockNum? atBlock}) async {
142 + final function = self.abi.functions[9];
143 + assert(checkSignature(function, 'b079f163'));
144 + final params = [];
145 + final response = await read(
146 + function,
147 + params,
148 + atBlock,
149 + );
150 + return (response[0] as BigInt);
151 + }
152 +
153 + /// The optional [atBlock] parameter can be used to view historical data. When
154 + /// set, the function will be evaluated in the specified block. By default, the
155 + /// latest on-chain block will be used.
156 + Future<_i1.EthereumAddress> deuro({_i1.BlockNum? atBlock}) async {
157 + final function = self.abi.functions[10];
158 + assert(checkSignature(function, '82b8eaf5'));
159 + final params = [];
160 + final response = await read(
161 + function,
162 + params,
163 + atBlock,
164 + );
165 + return (response[0] as _i1.EthereumAddress);
166 + }
167 +
168 + /// The optional [atBlock] parameter can be used to view historical data. When
169 + /// set, the function will be evaluated in the specified block. By default, the
170 + /// latest on-chain block will be used.
171 + Future<_i1.EthereumAddress> equity({_i1.BlockNum? atBlock}) async {
172 + final function = self.abi.functions[11];
173 + assert(checkSignature(function, '91a0ac6a'));
174 + final params = [];
175 + final response = await read(
176 + function,
177 + params,
178 + atBlock,
179 + );
180 + return (response[0] as _i1.EthereumAddress);
181 + }
182 +
183 + /// The optional [atBlock] parameter can be used to view historical data. When
184 + /// set, the function will be evaluated in the specified block. By default, the
185 + /// latest on-chain block will be used.
186 + Future<BigInt> nextChange({_i1.BlockNum? atBlock}) async {
187 + final function = self.abi.functions[12];
188 + assert(checkSignature(function, 'b6f83c17'));
189 + final params = [];
190 + final response = await read(
191 + function,
192 + params,
193 + atBlock,
194 + );
195 + return (response[0] as BigInt);
196 + }
197 +
198 + /// The optional [atBlock] parameter can be used to view historical data. When
199 + /// set, the function will be evaluated in the specified block. By default, the
200 + /// latest on-chain block will be used.
201 + Future<BigInt> nextRatePPM({_i1.BlockNum? atBlock}) async {
202 + final function = self.abi.functions[13];
203 + assert(checkSignature(function, '2e4b20ab'));
204 + final params = [];
205 + final response = await read(
206 + function,
207 + params,
208 + atBlock,
209 + );
210 + return (response[0] as BigInt);
211 + }
212 +
213 + /// The optional [transaction] parameter can be used to override parameters
214 + /// like the gas price, nonce and max gas. The `data` and `to` fields will be
215 + /// set by the contract.
216 + Future<String> refreshBalance(
217 + ({_i1.EthereumAddress owner}) args, {
218 + required _i1.Credentials credentials,
219 + _i1.Transaction? transaction,
220 + }) async {
221 + final function = self.abi.functions[15];
222 + assert(checkSignature(function, 'b77cd1c7'));
223 + final params = [args.owner];
224 + return write(
225 + credentials,
226 + transaction,
227 + function,
228 + params,
229 + );
230 + }
231 +
232 + /// The optional [transaction] parameter can be used to override parameters
233 + /// like the gas price, nonce and max gas. The `data` and `to` fields will be
234 + /// set by the contract.
235 + Future<String> refreshMyBalance({
236 + required _i1.Credentials credentials,
237 + _i1.Transaction? transaction,
238 + }) async {
239 + final function = self.abi.functions[16];
240 + assert(checkSignature(function, '85bd12d1'));
241 + final params = [];
242 + return write(
243 + credentials,
244 + transaction,
245 + function,
246 + params,
247 + );
248 + }
249 +
250 + /// The optional [transaction] parameter can be used to override parameters
251 + /// like the gas price, nonce and max gas. The `data` and `to` fields will be
252 + /// set by the contract.
253 + Future<_i2.Uint8List> save(
254 + ({BigInt amount, _i2.Uint8List frontendCode}) args, {
255 + required _i1.Credentials credentials,
256 + _i1.Transaction? transaction,
257 + }) async {
258 + final function = self.abi.functions[17];
259 + assert(checkSignature(function, '9e2363dc'));
260 + final params = [
261 + args.amount,
262 + args.frontendCode,
263 + ];
264 + return writeRaw(
265 + credentials,
266 + transaction,
267 + function,
268 + params,
269 + );
270 + }
271 +
272 + /// The optional [transaction] parameter can be used to override parameters
273 + /// like the gas price, nonce and max gas. The `data` and `to` fields will be
274 + /// set by the contract.
275 + Future<_i2.Uint8List> saveTo(
276 + ({
277 + _i1.EthereumAddress owner,
278 + BigInt amount,
279 + _i2.Uint8List frontendCode
280 + }) args, {
281 + required _i1.Credentials credentials,
282 + _i1.Transaction? transaction,
283 + }) async {
284 + final function = self.abi.functions[19];
285 + assert(checkSignature(function, 'cbcf9676'));
286 + final params = [
287 + args.owner,
288 + args.amount,
289 + args.frontendCode,
290 + ];
291 + return writeRaw(
292 + credentials,
293 + transaction,
294 + function,
295 + params,
296 + );
297 + }
298 +
299 + /// The optional [atBlock] parameter can be used to view historical data. When
300 + /// set, the function will be evaluated in the specified block. By default, the
301 + /// latest on-chain block will be used.
302 + Future<Savings> savings({
303 + required _i1.EthereumAddress accountOwner,
304 + _i1.BlockNum? atBlock,
305 + }) async {
306 + final function = self.abi.functions[21];
307 + assert(checkSignature(function, '1f7cdd5f'));
308 + final params = [accountOwner];
309 + final response = await read(
310 + function,
311 + params,
312 + atBlock,
313 + );
314 + return Savings(response);
315 + }
316 +
317 + /// The optional [transaction] parameter can be used to override parameters
318 + /// like the gas price, nonce and max gas. The `data` and `to` fields will be
319 + /// set by the contract.
320 + Future<_i2.Uint8List> withdraw(
321 + ({
322 + _i1.EthereumAddress target,
323 + BigInt amount,
324 + _i2.Uint8List frontendCode
325 + }) args, {
326 + required _i1.Credentials credentials,
327 + _i1.Transaction? transaction,
328 + }) async {
329 + final function = self.abi.functions[24];
330 + assert(checkSignature(function, '829a0476'));
331 + final params = [
332 + args.target,
333 + args.amount,
334 + args.frontendCode,
335 + ];
336 + return writeRaw(
337 + credentials,
338 + transaction,
339 + function,
340 + params,
341 + );
342 + }
343 +
344 + /// Returns a live stream of all InterestCollected events emitted by this contract.
345 + Stream<InterestCollected> interestCollectedEvents({
346 + _i1.BlockNum? fromBlock,
347 + _i1.BlockNum? toBlock,
348 + }) {
349 + final event = self.event('InterestCollected');
350 + final filter = _i1.FilterOptions.events(
351 + contract: self,
352 + event: event,
353 + fromBlock: fromBlock,
354 + toBlock: toBlock,
355 + );
356 + return client.events(filter).map((_i1.FilterEvent result) {
357 + final decoded = event.decodeResults(
358 + result.topics!,
359 + result.data!,
360 + );
361 + return InterestCollected(
362 + decoded,
363 + result,
364 + );
365 + });
366 + }
367 +
368 + /// Returns a live stream of all RateChanged events emitted by this contract.
369 + Stream<RateChanged> rateChangedEvents({
370 + _i1.BlockNum? fromBlock,
371 + _i1.BlockNum? toBlock,
372 + }) {
373 + final event = self.event('RateChanged');
374 + final filter = _i1.FilterOptions.events(
375 + contract: self,
376 + event: event,
377 + fromBlock: fromBlock,
378 + toBlock: toBlock,
379 + );
380 + return client.events(filter).map((_i1.FilterEvent result) {
381 + final decoded = event.decodeResults(
382 + result.topics!,
383 + result.data!,
384 + );
385 + return RateChanged(
386 + decoded,
387 + result,
388 + );
389 + });
390 + }
391 +
392 + /// Returns a live stream of all RateProposed events emitted by this contract.
393 + Stream<RateProposed> rateProposedEvents({
394 + _i1.BlockNum? fromBlock,
395 + _i1.BlockNum? toBlock,
396 + }) {
397 + final event = self.event('RateProposed');
398 + final filter = _i1.FilterOptions.events(
399 + contract: self,
400 + event: event,
401 + fromBlock: fromBlock,
402 + toBlock: toBlock,
403 + );
404 + return client.events(filter).map((_i1.FilterEvent result) {
405 + final decoded = event.decodeResults(
406 + result.topics!,
407 + result.data!,
408 + );
409 + return RateProposed(
410 + decoded,
411 + result,
412 + );
413 + });
414 + }
415 +
416 + /// Returns a live stream of all Saved events emitted by this contract.
417 + Stream<Saved> savedEvents({
418 + _i1.BlockNum? fromBlock,
419 + _i1.BlockNum? toBlock,
420 + }) {
421 + final event = self.event('Saved');
422 + final filter = _i1.FilterOptions.events(
423 + contract: self,
424 + event: event,
425 + fromBlock: fromBlock,
426 + toBlock: toBlock,
427 + );
428 + return client.events(filter).map((_i1.FilterEvent result) {
429 + final decoded = event.decodeResults(
430 + result.topics!,
431 + result.data!,
432 + );
433 + return Saved(
434 + decoded,
435 + result,
436 + );
437 + });
438 + }
439 +
440 + /// Returns a live stream of all Withdrawn events emitted by this contract.
441 + Stream<Withdrawn> withdrawnEvents({
442 + _i1.BlockNum? fromBlock,
443 + _i1.BlockNum? toBlock,
444 + }) {
445 + final event = self.event('Withdrawn');
446 + final filter = _i1.FilterOptions.events(
447 + contract: self,
448 + event: event,
449 + fromBlock: fromBlock,
450 + toBlock: toBlock,
451 + );
452 + return client.events(filter).map((_i1.FilterEvent result) {
453 + final decoded = event.decodeResults(
454 + result.topics!,
455 + result.data!,
456 + );
457 + return Withdrawn(
458 + decoded,
459 + result,
460 + );
461 + });
462 + }
463 +}
464 +
465 +class Savings {
466 + Savings(List<dynamic> response)
467 + : saved = (response[0] as BigInt),
468 + ticks = (response[1] as BigInt);
469 +
470 + final BigInt saved;
471 +
472 + final BigInt ticks;
473 +}
474 +
475 +class InterestCollected {
476 + InterestCollected(
477 + List<dynamic> response,
478 + this.event,
479 + ) : account = (response[0] as _i1.EthereumAddress),
480 + interest = (response[1] as BigInt);
481 +
482 + final _i1.EthereumAddress account;
483 +
484 + final BigInt interest;
485 +
486 + final _i1.FilterEvent event;
487 +}
488 +
489 +class RateChanged {
490 + RateChanged(
491 + List<dynamic> response,
492 + this.event,
493 + ) : newRate = (response[0] as BigInt);
494 +
495 + final BigInt newRate;
496 +
497 + final _i1.FilterEvent event;
498 +}
499 +
500 +class RateProposed {
501 + RateProposed(
502 + List<dynamic> response,
503 + this.event,
504 + ) : who = (response[0] as _i1.EthereumAddress),
505 + nextRate = (response[1] as BigInt),
506 + nextChange = (response[2] as BigInt);
507 +
508 + final _i1.EthereumAddress who;
509 +
510 + final BigInt nextRate;
511 +
512 + final BigInt nextChange;
513 +
514 + final _i1.FilterEvent event;
515 +}
516 +
517 +class Saved {
518 + Saved(
519 + List<dynamic> response,
520 + this.event,
521 + ) : account = (response[0] as _i1.EthereumAddress),
522 + amount = (response[1] as BigInt);
523 +
524 + final _i1.EthereumAddress account;
525 +
526 + final BigInt amount;
527 +
528 + final _i1.FilterEvent event;
529 +}
530 +
531 +class Withdrawn {
532 + Withdrawn(
533 + List<dynamic> response,
534 + this.event,
535 + ) : account = (response[0] as _i1.EthereumAddress),
536 + amount = (response[1] as BigInt);
537 +
538 + final _i1.EthereumAddress account;
539 +
540 + final BigInt amount;
541 +
542 + final _i1.FilterEvent event;
543 +}
cw_ethereum/pubspec.yaml
+1 -1
@@ -6,7 +6,7 @@ author: Cake Wallet
6 homepage: https://cakewallet.com
7
8 environment:
9 - sdk: '>=2.18.2 <3.0.0'
9 + sdk: ^3.5.0
10 flutter: ">=1.17.0"
11
12 dependencies:
cw_evm/lib/evm_chain_client.dart
+62 -2
@@ -76,7 +76,7 @@ abstract class EVMChainClient {
76 Future<int> getGasUnitPrice() async {
77 try {
78 final gasPrice = await _client!.getGasPrice();
79 -
79 +
80 return gasPrice.getInWei.toInt();
81 } catch (_) {
82 return 0;
@@ -101,6 +101,7 @@ abstract class EVMChainClient {
101 String? contractAddress,
102 EtherAmount? gasPrice,
103 EtherAmount? maxFeePerGas,
104 + Uint8List? data,
105 }) async {
106 try {
107 if (contractAddress == null) {
@@ -124,7 +125,7 @@ abstract class EVMChainClient {
125 final gasEstimate = await _client!.estimateGas(
126 sender: senderAddress,
127 to: EthereumAddress.fromHex(contractAddress),
127 - data: transfer.encodeCall([
128 + data: data ?? transfer.encodeCall([
129 toAddress,
130 value.getInWei,
131 ]),
@@ -137,6 +138,21 @@ abstract class EVMChainClient {
138 }
139 }
140
141 + Uint8List getEncodedDataForApprovalTransaction({
142 + required EthereumAddress toAddress,
143 + required EtherAmount value,
144 + required EthereumAddress contractAddress,
145 + }) {
146 + final contract = DeployedContract(ethereumContractAbi, contractAddress);
147 +
148 + final approve = contract.function('approve');
149 +
150 + return approve.encodeCall([
151 + toAddress,
152 + value.getInWei,
153 + ]);
154 + }
155 +
156 Future<PendingEVMChainTransaction> signTransaction({
157 required Credentials privateKey,
158 required String toAddress,
@@ -198,6 +214,50 @@ abstract class EVMChainClient {
214 );
215 }
216
217 + Future<PendingEVMChainTransaction> signApprovalTransaction({
218 + required Credentials privateKey,
219 + required String spender,
220 + required BigInt amount,
221 + required BigInt gasFee,
222 + required int estimatedGasUnits,
223 + required int maxFeePerGas,
224 + required EVMChainTransactionPriority priority,
225 + required int exponent,
226 + required String contractAddress,
227 + }) async {
228 +
229 + final Transaction transaction = createTransaction(
230 + from: privateKey.address,
231 + to: EthereumAddress.fromHex(contractAddress),
232 + maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
233 + amount: EtherAmount.zero(),
234 + maxGas: estimatedGasUnits,
235 + maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
236 + );
237 +
238 + final erc20 = ERC20(
239 + client: _client!,
240 + address: EthereumAddress.fromHex(contractAddress),
241 + chainId: chainId,
242 + );
243 +
244 + final signedTransaction = await erc20.approve(
245 + EthereumAddress.fromHex(spender),
246 + amount,
247 + credentials: privateKey,
248 + transaction: transaction,
249 + );
250 +
251 + return PendingEVMChainTransaction(
252 + signedTransaction: prepareSignedTransactionForSending(signedTransaction),
253 + amount: amount.toString(),
254 + fee: gasFee,
255 + sendTransaction: () => sendTransaction(signedTransaction),
256 + exponent: exponent,
257 + isInfiniteApproval: amount.toRadixString(16) == 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
258 + );
259 + }
260 +
261 Transaction createTransaction({
262 required EthereumAddress from,
263 required EthereumAddress to,
cw_evm/lib/evm_chain_wallet.dart
+40
@@ -2,6 +2,7 @@ import 'dart:async';
2 import 'dart:convert';
3 import 'dart:io';
4 import 'dart:math';
5 +import 'dart:typed_data';
6
7 import 'package:bip32/bip32.dart' as bip32;
8 import 'package:bip39/bip39.dart' as bip39;
@@ -255,6 +256,7 @@ abstract class EVMChainWalletBase
256 required String? contractAddress,
257 required String receivingAddressHex,
258 required TransactionPriority priority,
259 + Uint8List? data,
260 }) async {
261 try {
262 if (priority is EVMChainTransactionPriority) {
@@ -276,6 +278,7 @@ abstract class EVMChainWalletBase
278 gasPrice: EtherAmount.fromInt(EtherUnit.wei, gasPrice),
279 toAddress: EthereumAddress.fromHex(receivingAddressHex),
280 maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
281 + data: data,
282 );
283
284 final totalGasFee = estimatedGas * maxFeePerGas;
@@ -478,6 +481,43 @@ abstract class EVMChainWalletBase
481 return pendingEVMChainTransaction;
482 }
483
484 + Future<PendingTransaction> createApprovalTransaction(
485 + BigInt amount,
486 + String spender,
487 + CryptoCurrency token,
488 + EVMChainTransactionPriority priority) async {
489 + final CryptoCurrency transactionCurrency =
490 + balance.keys.firstWhere((element) => element.title == token.title);
491 + assert(transactionCurrency is Erc20Token);
492 +
493 + final data = _client.getEncodedDataForApprovalTransaction(
494 + contractAddress: EthereumAddress.fromHex(
495 + (transactionCurrency as Erc20Token).contractAddress),
496 + value: EtherAmount.fromBigInt(EtherUnit.wei, amount),
497 + toAddress: EthereumAddress.fromHex(spender),
498 + );
499 +
500 + final gasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
501 + amount: amount,
502 + receivingAddressHex: spender,
503 + priority: priority,
504 + contractAddress: transactionCurrency.contractAddress,
505 + data: data,
506 + );
507 +
508 + return _client.signApprovalTransaction(
509 + privateKey: _evmChainPrivateKey,
510 + spender: spender,
511 + amount: amount,
512 + priority: priority,
513 + gasFee: BigInt.from(gasFeesModel.estimatedGasFee),
514 + maxFeePerGas: gasFeesModel.maxFeePerGas,
515 + estimatedGasUnits: gasFeesModel.estimatedGasUnits,
516 + exponent: transactionCurrency.decimal,
517 + contractAddress: transactionCurrency.contractAddress,
518 + );
519 + }
520 +
521 Future<void> _updateTransactions() async {
522 try {
523 if (_isTransactionUpdating) {
cw_evm/lib/pending_evm_chain_transaction.dart
+3
@@ -11,6 +11,7 @@ class PendingEVMChainTransaction with PendingTransaction {
11 final BigInt fee;
12 final String amount;
13 final int exponent;
14 + final bool isInfiniteApproval;
15
16 PendingEVMChainTransaction({
17 required this.sendTransaction,
@@ -18,10 +19,12 @@ class PendingEVMChainTransaction with PendingTransaction {
19 required this.fee,
20 required this.amount,
21 required this.exponent,
22 + this.isInfiniteApproval = false,
23 });
24
25 @override
26 String get amountFormatted {
27 + if (isInfiniteApproval) return "∞";
28 final _amount = (BigInt.parse(amount) / BigInt.from(pow(10, exponent))).toString();
29 return _amount.substring(0, min(10, _amount.length));
30 }
lib/di.dart
+7 -1
@@ -35,6 +35,7 @@ import 'package:cake_wallet/src/screens/dev/monero_background_sync.dart';
35 import 'package:cake_wallet/src/screens/dev/moneroc_call_profiler.dart';
36 import 'package:cake_wallet/src/screens/dev/secure_preferences_page.dart';
37 import 'package:cake_wallet/src/screens/dev/shared_preferences_page.dart';
38 +import 'package:cake_wallet/src/screens/integrations/deuro/savings_page.dart';
39 import 'package:cake_wallet/src/screens/settings/background_sync_page.dart';
40 import 'package:cake_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart';
41 import 'package:cake_wallet/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart';
@@ -43,6 +44,7 @@ import 'package:cake_wallet/themes/core/theme_store.dart';
44 import 'package:cake_wallet/view_model/dev/monero_background_sync.dart';
45 import 'package:cake_wallet/view_model/dev/secure_preferences.dart';
46 import 'package:cake_wallet/view_model/dev/shared_preferences.dart';
47 +import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
48 import 'package:cake_wallet/view_model/link_view_model.dart';
49 import 'package:cake_wallet/tron/tron.dart';
50 import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
@@ -1510,6 +1512,10 @@ Future<void> setup({
1512 getIt.registerFactory(() => BackgroundSyncLogsViewModel());
1513
1514 getIt.registerFactory(() => DevBackgroundSyncLogsPage(getIt.get<BackgroundSyncLogsViewModel>()));
1513 -
1515 +
1516 + getIt.registerFactory(() => DEuroViewModel(getIt<AppStore>()));
1517 +
1518 + getIt.registerFactory(() => DEuroSavingsPage(getIt<DEuroViewModel>()));
1519 +
1520 _isSetupFinished = true;
1521 }
lib/ethereum/cw_ethereum.dart
+57 -30
@@ -67,8 +67,7 @@ class CWEthereum extends Ethereum {
67 @override
68 String getPublicKey(WalletBase wallet) {
69 final privateKeyInUnitInt = (wallet as EthereumWallet).evmChainPrivateKey;
70 - final publicKey = privateKeyInUnitInt.address.hex;
71 - return publicKey;
70 + return privateKeyInUnitInt.address.hex;
71 }
72
73 @override
@@ -138,29 +137,24 @@ class CWEthereum extends Ethereum {
137 }
138
139 @override
141 - List<Erc20Token> getERC20Currencies(WalletBase wallet) {
142 - final ethereumWallet = wallet as EthereumWallet;
143 - return ethereumWallet.erc20Currencies;
144 - }
140 + List<Erc20Token> getERC20Currencies(WalletBase wallet) =>
141 + (wallet as EthereumWallet).erc20Currencies;
142
143 @override
147 - Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) async {
148 - await (wallet as EthereumWallet).addErc20Token(token as Erc20Token);
149 - }
144 + Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) =>
145 + (wallet as EthereumWallet).addErc20Token(token as Erc20Token);
146
147 @override
152 - Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) async =>
153 - await (wallet as EthereumWallet).deleteErc20Token(token as Erc20Token);
148 + Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) =>
149 + (wallet as EthereumWallet).deleteErc20Token(token as Erc20Token);
150
151 @override
156 - Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) async =>
157 - await (wallet as EthereumWallet).removeTokenTransactionsInHistory(token as Erc20Token);
152 + Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) =>
153 + (wallet as EthereumWallet).removeTokenTransactionsInHistory(token as Erc20Token);
154
155 @override
160 - Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) async {
161 - final ethereumWallet = wallet as EthereumWallet;
162 - return await ethereumWallet.getErc20Token(contractAddress, 'eth');
163 - }
156 + Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) =>
157 + (wallet as EthereumWallet).getErc20Token(contractAddress, 'eth');
158
159 @override
160 CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
@@ -177,23 +171,19 @@ class CWEthereum extends Ethereum {
171 }
172
173 @override
180 - void updateEtherscanUsageState(WalletBase wallet, bool isEnabled) {
181 - (wallet as EthereumWallet).updateScanProviderUsageState(isEnabled);
182 - }
174 + void updateEtherscanUsageState(WalletBase wallet, bool isEnabled) =>
175 + (wallet as EthereumWallet).updateScanProviderUsageState(isEnabled);
176
177 @override
185 - Web3Client? getWeb3Client(WalletBase wallet) {
186 - return (wallet as EthereumWallet).getWeb3Client();
187 - }
178 + Web3Client? getWeb3Client(WalletBase wallet) => (wallet as EthereumWallet).getWeb3Client();
179
180 + @override
181 String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
182
183 @override
192 - void setLedgerConnection(
193 - WalletBase wallet, ledger.LedgerConnection connection) {
184 + void setLedgerConnection(WalletBase wallet, ledger.LedgerConnection connection) {
185 ((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials)
195 - .setLedgerConnection(
196 - connection, wallet.walletInfo.derivationInfo?.derivationPath);
186 + .setLedgerConnection(connection, wallet.walletInfo.derivationInfo?.derivationPath);
187 }
188
189 @override
@@ -209,7 +199,44 @@ class CWEthereum extends Ethereum {
199 }
200
201 @override
212 - List<String> getDefaultTokenContractAddresses() {
213 - return DefaultEthereumErc20Tokens().initialErc20Tokens.map((e) => e.contractAddress).toList();
214 - }
202 + List<String> getDefaultTokenContractAddresses() =>
203 + DefaultEthereumErc20Tokens().initialErc20Tokens.map((e) => e.contractAddress).toList();
204 +
205 + Future<PendingTransaction> createTokenApproval(WalletBase wallet, BigInt amount, String spender,
206 + CryptoCurrency token, TransactionPriority priority) =>
207 + (wallet as EVMChainWallet).createApprovalTransaction(
208 + amount, spender, token, priority as EVMChainTransactionPriority);
209 +
210 + // Integrations
211 + @override
212 + Future<BigInt> getDEuroSavingsBalance(WalletBase wallet) =>
213 + DEuro(wallet as EthereumWallet).savingsBalance;
214 +
215 + @override
216 + Future<BigInt> getDEuroAccruedInterest(WalletBase wallet) =>
217 + DEuro(wallet as EthereumWallet).accruedInterest;
218 +
219 + @override
220 + Future<BigInt> getDEuroInterestRate(WalletBase wallet) =>
221 + DEuro(wallet as EthereumWallet).interestRate;
222 +
223 + @override
224 + Future<BigInt> getDEuroSavingsApproved(WalletBase wallet) =>
225 + DEuro(wallet as EthereumWallet).approvedBalance;
226 +
227 + @override
228 + Future<PendingTransaction> addDEuroSaving(
229 + WalletBase wallet, BigInt amount, TransactionPriority priority) =>
230 + DEuro(wallet as EthereumWallet)
231 + .depositSavings(amount, priority as EVMChainTransactionPriority);
232 +
233 + @override
234 + Future<PendingTransaction> removeDEuroSaving(
235 + WalletBase wallet, BigInt amount, TransactionPriority priority) =>
236 + DEuro(wallet as EthereumWallet)
237 + .withdrawSavings(amount, priority as EVMChainTransactionPriority);
238 +
239 + @override
240 + Future<PendingTransaction> enableDEuroSaving(WalletBase wallet, TransactionPriority priority) =>
241 + DEuro(wallet as EthereumWallet).enableSavings(priority as EVMChainTransactionPriority);
242 }
lib/polygon/cw_polygon.dart
+33 -28
@@ -67,8 +67,7 @@ class CWPolygon extends Polygon {
67 @override
68 String getPublicKey(WalletBase wallet) {
69 final privateKeyInUnitInt = (wallet as PolygonWallet).evmChainPrivateKey;
70 - final publicKey = privateKeyInUnitInt.address.hex;
71 - return publicKey;
70 + return privateKeyInUnitInt.address.hex;
71 }
72
73 @override
@@ -137,28 +136,27 @@ class CWPolygon extends Polygon {
136 }
137
138 @override
140 - List<Erc20Token> getERC20Currencies(WalletBase wallet) {
141 - final polygonWallet = wallet as PolygonWallet;
142 - return polygonWallet.erc20Currencies;
143 - }
139 + List<Erc20Token> getERC20Currencies(WalletBase wallet) =>
140 + (wallet as PolygonWallet).erc20Currencies;
141
142 @override
146 - Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) async =>
147 - await (wallet as PolygonWallet).addErc20Token(token as Erc20Token);
143 + Future<void> addErc20Token(WalletBase wallet, CryptoCurrency token) =>
144 + (wallet as PolygonWallet).addErc20Token(token as Erc20Token);
145
146 @override
150 - Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) async =>
151 - await (wallet as PolygonWallet).deleteErc20Token(token as Erc20Token);
147 + Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token) =>
148 + (wallet as PolygonWallet).deleteErc20Token(token as Erc20Token);
149
150 @override
154 - Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) async =>
155 - await (wallet as PolygonWallet).removeTokenTransactionsInHistory(token as Erc20Token);
151 + Future<void> removeTokenTransactionsInHistory(
152 + WalletBase wallet, CryptoCurrency token) =>
153 + (wallet as PolygonWallet)
154 + .removeTokenTransactionsInHistory(token as Erc20Token);
155
156 @override
158 - Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) async {
159 - final polygonWallet = wallet as PolygonWallet;
160 - return await polygonWallet.getErc20Token(contractAddress, 'polygon');
161 - }
157 + Future<Erc20Token?> getErc20Token(
158 + WalletBase wallet, String contractAddress) =>
159 + (wallet as PolygonWallet).getErc20Token(contractAddress, 'polygon');
160
161 @override
162 CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
@@ -176,23 +174,29 @@ class CWPolygon extends Polygon {
174 }
175
176 @override
179 - void updatePolygonScanUsageState(WalletBase wallet, bool isEnabled) {
180 - (wallet as PolygonWallet).updateScanProviderUsageState(isEnabled);
181 - }
177 + void updatePolygonScanUsageState(WalletBase wallet, bool isEnabled) =>
178 + (wallet as PolygonWallet).updateScanProviderUsageState(isEnabled);
179
180 @override
184 - Web3Client? getWeb3Client(WalletBase wallet) {
185 - return (wallet as PolygonWallet).getWeb3Client();
186 - }
181 + Web3Client? getWeb3Client(WalletBase wallet) =>
182 + (wallet as PolygonWallet).getWeb3Client();
183
188 - String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
184 + @override
185 + String getTokenAddress(CryptoCurrency asset) =>
186 + (asset as Erc20Token).contractAddress;
187 +
188 + @override
189 + Future<PendingTransaction> createTokenApproval(WalletBase wallet,
190 + BigInt amount, String spender, CryptoCurrency token, TransactionPriority priority) =>
191 + (wallet as EVMChainWallet)
192 + .createApprovalTransaction(amount, spender, token, priority as EVMChainTransactionPriority);
193
194 @override
195 void setLedgerConnection(
196 WalletBase wallet, ledger.LedgerConnection connection) {
197 ((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials)
198 .setLedgerConnection(
195 - connection, wallet.walletInfo.derivationInfo?.derivationPath);
199 + connection, wallet.walletInfo.derivationInfo?.derivationPath);
200 }
201
202 @override
@@ -206,9 +210,10 @@ class CWPolygon extends Polygon {
210 throw err;
211 }
212 }
209 -
213 +
214 @override
211 - List<String> getDefaultTokenContractAddresses() {
212 - return DefaultPolygonErc20Tokens().initialPolygonErc20Tokens.map((e) => e.contractAddress).toList();
213 - }
215 + List<String> getDefaultTokenContractAddresses() => DefaultPolygonErc20Tokens()
216 + .initialPolygonErc20Tokens
217 + .map((e) => e.contractAddress)
218 + .toList();
219 }
lib/router.dart
+6
@@ -48,6 +48,7 @@ import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dar
48 import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_external_send_page.dart';
49 import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
50 import 'package:cake_wallet/src/screens/faq/faq_page.dart';
51 +import 'package:cake_wallet/src/screens/integrations/deuro/savings_page.dart';
52 import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
53 import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
54 import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
@@ -920,6 +921,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
921 builder: (_) => getIt.get<DevSecurePreferencesPage>(),
922 );
923
924 + case Routes.dEuroSavings:
925 + return MaterialPageRoute<void>(
926 + builder: (_) => getIt.get<DEuroSavingsPage>(),
927 + );
928 +
929 default:
930 return MaterialPageRoute<void>(
931 builder: (_) => Scaffold(
lib/routes.dart
+2
@@ -127,4 +127,6 @@ class Routes {
127 static const walletGroupExistingSeedDescriptionPage = '/wallet_group_existing_seed_description_page';
128 static const walletSeedVerificationPage = '/wallet_seed_verification_page';
129 static const exchangeTradeExternalSendPage = '/exchange_trade_external_send_page';
130 +
131 + static const dEuroSavings = '/integration/dEuro/savings';
132 }
lib/src/screens/dashboard/pages/cake_features_page.dart
+20 -2
@@ -5,15 +5,16 @@ import 'package:cake_wallet/routes.dart';
5 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
6 import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
7 import 'package:cake_wallet/utils/show_pop_up.dart';
8 +import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
9 import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
10 import 'package:cw_core/utils/print_verbose.dart';
11 import 'package:cw_core/wallet_type.dart';
11 -import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
12 import 'package:flutter/material.dart';
13 import 'package:url_launcher/url_launcher.dart';
14
15 class CakeFeaturesPage extends StatelessWidget {
16 - CakeFeaturesPage({required this.dashboardViewModel, required this.cakeFeaturesViewModel});
16 + CakeFeaturesPage(
17 + {required this.dashboardViewModel, required this.cakeFeaturesViewModel});
18
19 final DashboardViewModel dashboardViewModel;
20 final CakeFeaturesViewModel cakeFeaturesViewModel;
@@ -58,6 +59,23 @@ class CakeFeaturesPage extends StatelessWidget {
59 fit: BoxFit.cover,
60 ),
61 ),
62 + if (dashboardViewModel.type == WalletType.ethereum) ...[
63 + DashBoardRoundedCardWidget(
64 + isDarkTheme: dashboardViewModel.isDarkTheme,
65 + shadowBlur: dashboardViewModel.getShadowBlur(),
66 + shadowSpread: dashboardViewModel.getShadowSpread(),
67 + onTap: () =>
68 + Navigator.of(context).pushNamed(Routes.dEuroSavings),
69 + title: S.of(context).deuro_savings,
70 + subTitle: S.of(context).deuro_savings_subtitle,
71 + image: Image.asset(
72 + 'assets/images/deuro_icon.png',
73 + height: 80,
74 + width: 80,
75 + fit: BoxFit.cover,
76 + ),
77 + ),
78 + ],
79 DashBoardRoundedCardWidget(
80 isDarkTheme: dashboardViewModel.isDarkTheme,
81 shadowBlur: dashboardViewModel.getShadowBlur(),
lib/src/screens/integrations/deuro/savings_page.dart new
+197
@@ -0,0 +1,197 @@
1 +import 'package:cake_wallet/core/execution_state.dart';
2 +import 'package:cake_wallet/generated/i18n.dart';
3 +import 'package:cake_wallet/src/screens/base_page.dart';
4 +import 'package:cake_wallet/src/screens/integrations/deuro/widgets/interest_card_widget.dart';
5 +import 'package:cake_wallet/src/screens/integrations/deuro/widgets/savings_card_widget.dart';
6 +import 'package:cake_wallet/src/screens/integrations/deuro/widgets/savings_edit_sheet.dart';
7 +import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart';
8 +import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
9 +import 'package:cake_wallet/src/widgets/gradient_background.dart';
10 +import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
11 +import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
12 +import 'package:cw_core/crypto_currency.dart';
13 +import 'package:cw_core/pending_transaction.dart';
14 +import 'package:cw_core/wallet_type.dart';
15 +import 'package:flutter/material.dart';
16 +import 'package:flutter_mobx/flutter_mobx.dart';
17 +import 'package:mobx/mobx.dart';
18 +
19 +class DEuroSavingsPage extends BasePage {
20 + final DEuroViewModel _dEuroViewModel;
21 +
22 + DEuroSavingsPage(this._dEuroViewModel);
23 +
24 + @override
25 + bool get gradientBackground => true;
26 +
27 + @override
28 + Widget Function(BuildContext, Widget) get rootWrapper =>
29 + (context, scaffold) => GradientBackground(scaffold: scaffold);
30 +
31 + @override
32 + String get title => S.current.deuro_savings;
33 +
34 + Widget trailing(BuildContext context) => MergeSemantics(
35 + child: SizedBox(
36 + height: 37,
37 + width: 37,
38 + child: ButtonTheme(
39 + minWidth: double.minPositive,
40 + child: Semantics(
41 + label: "Refresh",
42 + child: TextButton(
43 + style: TextButton.styleFrom(
44 + foregroundColor: Theme.of(context).colorScheme.onSurface,
45 + overlayColor: WidgetStateColor.resolveWith(
46 + (states) => Colors.transparent),
47 + ),
48 + onPressed: _dEuroViewModel.reloadSavingsUserData,
49 + child: Icon(
50 + Icons.refresh,
51 + color: pageIconColor(context),
52 + size: 20,
53 + ),
54 + ),
55 + ),
56 + ),
57 + ),
58 + );
59 +
60 + @override
61 + Widget body(BuildContext context) {
62 + WidgetsBinding.instance
63 + .addPostFrameCallback((_) => _setReactions(context, _dEuroViewModel));
64 +
65 + return Container(
66 + width: double.infinity,
67 + child: Column(
68 + children: <Widget>[
69 + Observer(
70 + builder: (_) => SavingsCard(
71 + isDarkTheme: currentTheme.isDark,
72 + interestRate: "${_dEuroViewModel.interestRate}%",
73 + savingsBalance: _dEuroViewModel.savingsBalance,
74 + currency: CryptoCurrency.deuro,
75 + onAddSavingsPressed: () => _onSavingsAdd(context),
76 + onRemoveSavingsPressed: () => _onSavingsRemove(context),
77 + onApproveSavingsPressed: _dEuroViewModel.prepareApproval,
78 + isEnabled: _dEuroViewModel.isEnabled,
79 + ),
80 + ),
81 + Observer(
82 + builder: (_) => InterestCardWidget(
83 + isDarkTheme: currentTheme.isDark,
84 + title: S.of(context).deuro_savings_collect_interest,
85 + collectedInterest: _dEuroViewModel.accruedInterest,
86 + onCollectInterest: _dEuroViewModel.prepareCollectInterest,
87 + ),
88 + ),
89 + ],
90 + ),
91 + );
92 + }
93 +
94 + Future<void> _onSavingsAdd(BuildContext context) async {
95 + final amount = await Navigator.of(context).push(MaterialPageRoute<String>(
96 + builder: (BuildContext context) => SavingEditPage(isAdding: true)));
97 + if (amount != null) _dEuroViewModel.prepareSavingsEdit(amount, true);
98 + }
99 +
100 + Future<void> _onSavingsRemove(BuildContext context) async {
101 + final amount = await Navigator.of(context).push(MaterialPageRoute<String>(
102 + builder: (BuildContext context) => SavingEditPage(isAdding: false)));
103 + if (amount != null) _dEuroViewModel.prepareSavingsEdit(amount, false);
104 + }
105 +
106 + bool _isReactionsSet = false;
107 +
108 + void _setReactions(BuildContext context, DEuroViewModel dEuroViewModel) {
109 + if (_isReactionsSet) return;
110 +
111 + reaction((_) => dEuroViewModel.transaction, (PendingTransaction? tx) async {
112 + if (tx == null) return;
113 + final result = await showModalBottomSheet<bool>(
114 + context: context,
115 + isDismissible: false,
116 + isScrollControlled: true,
117 + builder: (BuildContext bottomSheetContext) => ConfirmSendingBottomSheet(
118 + key: ValueKey('savings_page_confirm_sending_dialog_key'),
119 + titleText: S.of(bottomSheetContext).confirm_transaction,
120 + currentTheme: currentTheme,
121 + walletType: WalletType.ethereum,
122 + titleIconPath: CryptoCurrency.deuro.iconPath,
123 + currency: CryptoCurrency.deuro,
124 + amount: S.of(bottomSheetContext).send_amount,
125 + amountValue: tx.amountFormatted,
126 + fiatAmountValue: "",
127 + fee: S.of(bottomSheetContext).send_estimated_fee,
128 + feeValue: tx.feeFormatted,
129 + feeFiatAmount: "",
130 + outputs: [],
131 + onSlideComplete: () async {
132 + Navigator.of(bottomSheetContext).pop(true);
133 + dEuroViewModel.commitTransaction();
134 + },
135 + change: tx.change,
136 + ),
137 + );
138 +
139 + if (result == null) dEuroViewModel.dismissTransaction();
140 + });
141 +
142 + reaction((_) => dEuroViewModel.approvalTransaction, (PendingTransaction? tx) async {
143 + if (tx == null) return;
144 + final result = await showModalBottomSheet<bool>(
145 + context: context,
146 + isDismissible: false,
147 + isScrollControlled: true,
148 + builder: (BuildContext bottomSheetContext) => ConfirmSendingBottomSheet(
149 + key: ValueKey('savings_page_confirm_approval_dialog_key'),
150 + titleText: S.of(bottomSheetContext).approve_tokens,
151 + currentTheme: currentTheme,
152 + walletType: WalletType.ethereum,
153 + titleIconPath: CryptoCurrency.deuro.iconPath,
154 + currency: CryptoCurrency.deuro,
155 + amount: S.of(bottomSheetContext).send_amount,
156 + amountValue: tx.amountFormatted,
157 + fiatAmountValue: "",
158 + fee: S.of(bottomSheetContext).send_estimated_fee,
159 + feeValue: tx.feeFormatted,
160 + feeFiatAmount: "",
161 + outputs: [],
162 + onSlideComplete: () {
163 + Navigator.of(bottomSheetContext).pop(true);
164 + dEuroViewModel.commitApprovalTransaction();
165 + },
166 + change: tx.change,
167 + ),
168 + );
169 +
170 + if (result == null) dEuroViewModel.dismissTransaction();
171 + });
172 +
173 + reaction((_) => dEuroViewModel.state, (ExecutionState state) async {
174 + if (state is TransactionCommitted) {
175 + WidgetsBinding.instance.addPostFrameCallback((_) async {
176 + if (!context.mounted) return;
177 +
178 + await showModalBottomSheet<void>(
179 + context: context,
180 + isDismissible: false,
181 + builder: (BuildContext bottomSheetContext) => InfoBottomSheet(
182 + currentTheme: currentTheme,
183 + titleText: S.of(bottomSheetContext).transaction_sent,
184 + contentImage: 'assets/images/birthday_cake.png',
185 + content: S.of(bottomSheetContext).deuro_tx_commited_content,
186 + actionButtonText: S.of(bottomSheetContext).close,
187 + actionButtonKey: ValueKey('send_page_sent_dialog_ok_button_key'),
188 + actionButton: () => Navigator.of(bottomSheetContext).pop(),
189 + ),
190 + );
191 + });
192 + }
193 + });
194 +
195 + _isReactionsSet = true;
196 + }
197 +}
lib/src/screens/integrations/deuro/widgets/edit_savings_bottom_sheet.dart new
+45
@@ -0,0 +1,45 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dart';
3 +import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
4 +import 'package:cake_wallet/src/widgets/primary_button.dart';
5 +import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
6 +import 'package:cw_core/crypto_currency.dart';
7 +import 'package:flutter/material.dart';
8 +
9 +class EditSavingsBottomSheet extends BaseBottomSheet {
10 + EditSavingsBottomSheet(this.dEuroViewModel, {required super.titleText});
11 +
12 + final _amountController = TextEditingController();
13 + final DEuroViewModel dEuroViewModel;
14 +
15 + @override
16 + Widget contentWidget(BuildContext context) => Column(
17 + children: [
18 + Padding(
19 + padding: EdgeInsets.symmetric(horizontal: 10),
20 + child: CurrencyAmountTextField(
21 + hasUnderlineBorder: true,
22 + borderWidth: 1.0,
23 + selectedCurrency: CryptoCurrency.deuro.name.toUpperCase(),
24 + amountFocusNode: null,
25 + amountController: _amountController,
26 + tag: CryptoCurrency.deuro.tag,
27 + isAmountEditable: true,
28 + ),
29 + ),
30 + ],
31 + );
32 +
33 + @override
34 + Widget footerWidget(BuildContext context) => Padding(
35 + padding: const EdgeInsets.fromLTRB(16, 12, 16, 34),
36 + child: LoadingPrimaryButton(
37 + onPressed: () => dEuroViewModel.prepareSavingsEdit(_amountController.text, true),
38 + text: S.of(context).confirm,
39 + color: Theme.of(context).colorScheme.primary,
40 + textColor: Theme.of(context).colorScheme.onPrimary,
41 + isLoading: false,
42 + isDisabled: false,
43 + ),
44 + );
45 +}
lib/src/screens/integrations/deuro/widgets/interest_card_widget.dart new
+67
@@ -0,0 +1,67 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:cake_wallet/src/screens/integrations/deuro/widgets/savings_card_widget.dart';
3 +import 'package:cake_wallet/themes/utils/custom_theme_colors.dart';
4 +import 'package:cw_core/crypto_currency.dart';
5 +import 'package:flutter/material.dart';
6 +
7 +class InterestCardWidget extends StatelessWidget {
8 + InterestCardWidget({
9 + required this.title,
10 + required this.collectedInterest,
11 + super.key,
12 + required this.isDarkTheme,
13 + required this.onCollectInterest,
14 + });
15 +
16 + final String title;
17 + final String collectedInterest;
18 + final bool isDarkTheme;
19 + final VoidCallback onCollectInterest;
20 +
21 + @override
22 + Widget build(BuildContext context) {
23 + return Stack(children: [
24 + Container(
25 + margin: EdgeInsets.symmetric(horizontal: 16),
26 + width: double.infinity,
27 + decoration: BoxDecoration(
28 + borderRadius: BorderRadius.circular(15),
29 + gradient: LinearGradient(
30 + colors: [
31 + isDarkTheme
32 + ? CustomThemeColors.cardGradientColorPrimaryDark
33 + : CustomThemeColors.cardGradientColorPrimaryLight,
34 + isDarkTheme
35 + ? CustomThemeColors.cardGradientColorSecondaryDark
36 + : CustomThemeColors.cardGradientColorSecondaryLight,
37 + ],
38 + begin: Alignment.topCenter,
39 + end: Alignment.bottomCenter,
40 + ),
41 + ),
42 + child: Padding(
43 + padding: EdgeInsets.all(20),
44 + child: Column(
45 + children: [
46 + SavingsCard.getAssetBalanceRow(
47 + context,
48 + title: title,
49 + subtitle: collectedInterest,
50 + currency: CryptoCurrency.deuro,
51 + hideSymbol: true,
52 + ),
53 + SizedBox(height: 10),
54 + SavingsCard.getButton(
55 + context,
56 + label: S.of(context).deuro_collect_interest,
57 + onPressed: onCollectInterest,
58 + backgroundColor: Theme.of(context).colorScheme.primary,
59 + color: Theme.of(context).colorScheme.onPrimary,
60 + ),
61 + ],
62 + ),
63 + ),
64 + ),
65 + ]);
66 + }
67 +}
lib/src/screens/integrations/deuro/widgets/numpad.dart new
+107
@@ -0,0 +1,107 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:flutter/material.dart';
3 +import 'package:flutter/services.dart';
4 +
5 +class NumberPad extends StatelessWidget {
6 + final VoidCallback? onDecimalPressed;
7 + final VoidCallback onDeletePressed;
8 + final void Function(int index) onNumberPressed;
9 + final FocusNode focusNode;
10 +
11 + const NumberPad({
12 + super.key,
13 + required this.onNumberPressed,
14 + required this.onDeletePressed,
15 + required this.focusNode,
16 + this.onDecimalPressed,
17 + });
18 +
19 + @override
20 + Widget build(BuildContext context) => KeyboardListener(
21 + focusNode: focusNode,
22 + onKeyEvent: (keyEvent) {
23 + if (keyEvent is KeyDownEvent) {
24 + if (keyEvent.logicalKey.keyLabel == "Backspace") {
25 + return onDeletePressed();
26 + }
27 +
28 + if ([".", ","].contains(keyEvent.logicalKey.keyLabel) &&
29 + onDecimalPressed != null) {
30 + return onDecimalPressed!();
31 + }
32 +
33 + int? number = int.tryParse(keyEvent.character ?? '');
34 + if (number != null) return onNumberPressed(number);
35 + }
36 + },
37 + child: SizedBox(
38 + height: 300,
39 + child: GridView.count(
40 + childAspectRatio: 2,
41 + shrinkWrap: true,
42 + crossAxisCount: 3,
43 + physics: const NeverScrollableScrollPhysics(),
44 + children: List.generate(12, (index) {
45 + if (index == 9) {
46 + if (onDecimalPressed == null) return Container();
47 + return InkWell(
48 + onTap: onDecimalPressed,
49 + child: Center(
50 + child: Text(
51 + '.',
52 + style: Theme.of(context).textTheme.headlineMedium?.copyWith(
53 + fontWeight: FontWeight.w600,
54 + fontSize: 30,
55 + color: Theme.of(context).colorScheme.onSurfaceVariant,
56 + ),
57 + textAlign: TextAlign.center,
58 + ),
59 + ),
60 + );
61 + } else if (index == 10) {
62 + index = 0;
63 + } else if (index == 11) {
64 + return MergeSemantics(
65 + child: Container(
66 + child: Semantics(
67 + label: S.of(context).delete,
68 + button: true,
69 + onTap: onDeletePressed,
70 + child: TextButton(
71 + onPressed: onDeletePressed,
72 + style: TextButton.styleFrom(
73 + backgroundColor:
74 + Colors.transparent,
75 + shape: CircleBorder(),
76 + ),
77 + child: Image.asset(
78 + 'assets/images/delete_icon.png',
79 + color: Theme.of(context).colorScheme.primary,
80 + ),
81 + ),
82 + ),
83 + ),
84 + );
85 + } else {
86 + index++;
87 + }
88 +
89 + return InkWell(
90 + onTap: () => onNumberPressed(index),
91 + child: Center(
92 + child: Text(
93 + '$index',
94 + style: Theme.of(context).textTheme.headlineMedium?.copyWith(
95 + fontWeight: FontWeight.w600,
96 + fontSize: 30,
97 + color: Theme.of(context).colorScheme.onSurfaceVariant,
98 + ),
99 + textAlign: TextAlign.center,
100 + ),
101 + ),
102 + );
103 + }),
104 + ),
105 + ),
106 + );
107 +}
lib/src/screens/integrations/deuro/widgets/savings_card_widget.dart new
+263
@@ -0,0 +1,263 @@
1 +import 'dart:math';
2 +
3 +import 'package:auto_size_text/auto_size_text.dart';
4 +import 'package:cake_wallet/generated/i18n.dart';
5 +import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
6 +import 'package:cake_wallet/themes/utils/custom_theme_colors.dart';
7 +import 'package:cw_core/crypto_currency.dart';
8 +import 'package:flutter/material.dart';
9 +
10 +class SavingsCard extends StatelessWidget {
11 + final bool isDarkTheme;
12 + final bool isEnabled;
13 + final String interestRate;
14 + final String savingsBalance;
15 + final CryptoCurrency currency;
16 + final VoidCallback onAddSavingsPressed;
17 + final VoidCallback onRemoveSavingsPressed;
18 + final VoidCallback onApproveSavingsPressed;
19 +
20 + const SavingsCard({
21 + super.key,
22 + required this.isDarkTheme,
23 + required this.interestRate,
24 + required this.savingsBalance,
25 + required this.currency,
26 + required this.onAddSavingsPressed,
27 + required this.onRemoveSavingsPressed,
28 + required this.onApproveSavingsPressed,
29 + this.isEnabled = true,
30 + });
31 +
32 + @override
33 + Widget build(BuildContext context) => Container(
34 + margin: const EdgeInsets.all(15),
35 + decoration: BoxDecoration(
36 + borderRadius: BorderRadius.circular(15),
37 + gradient: LinearGradient(
38 + colors: [
39 + isDarkTheme
40 + ? CustomThemeColors.cardGradientColorPrimaryDark
41 + : CustomThemeColors.cardGradientColorPrimaryLight,
42 + isDarkTheme
43 + ? CustomThemeColors.cardGradientColorSecondaryDark
44 + : CustomThemeColors.cardGradientColorSecondaryLight,
45 + ],
46 + begin: Alignment.topCenter,
47 + end: Alignment.bottomCenter,
48 + ),
49 + ),
50 + child: Container(
51 + padding: const EdgeInsets.all(20),
52 + child: Column(
53 + children: [
54 + getAssetBalanceRow(context,
55 + title: S.of(context).deuro_savings_balance,
56 + subtitle: savingsBalance,
57 + currency: currency),
58 + Padding(
59 + padding: const EdgeInsets.symmetric(vertical: 10),
60 + child: Row(
61 + children: [
62 + Expanded(
63 + child: Text(
64 + 'Current APR',
65 + style: Theme.of(context).textTheme.bodyMedium?.copyWith(
66 + color:
67 + Theme.of(context).colorScheme.onSurfaceVariant,
68 + fontWeight: FontWeight.w500,
69 + ),
70 + softWrap: true,
71 + ),
72 + ),
73 + Text(
74 + interestRate,
75 + style: Theme.of(context).textTheme.bodyMedium?.copyWith(
76 + color: Theme.of(context).colorScheme.onSurfaceVariant,
77 + fontWeight: FontWeight.w500,
78 + ),
79 + softWrap: true,
80 + ),
81 + ],
82 + ),
83 + ),
84 + Row(
85 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
86 + children: isEnabled
87 + ? [
88 + Expanded(
89 + child: getButton(
90 + context,
91 + label: S.of(context).deuro_savings_add,
92 + imagePath: 'assets/images/received.png',
93 + onPressed: onAddSavingsPressed,
94 + backgroundColor:
95 + Theme.of(context).colorScheme.primary,
96 + color: Theme.of(context).colorScheme.onPrimary,
97 + ),
98 + ),
99 + SizedBox(width: 12),
100 + Expanded(
101 + child: getButton(
102 + context,
103 + label: S.of(context).deuro_savings_remove,
104 + imagePath: 'assets/images/upload.png',
105 + onPressed: onRemoveSavingsPressed,
106 + backgroundColor:
107 + Theme.of(context).colorScheme.surface,
108 + color: Theme.of(context)
109 + .colorScheme
110 + .onSecondaryContainer,
111 + ),
112 + ),
113 + ]
114 + : [
115 + Expanded(
116 + child: getButton(
117 + context,
118 + label: S.of(context).deuro_savings_set_approval,
119 + onPressed: onApproveSavingsPressed,
120 + backgroundColor:
121 + Theme.of(context).colorScheme.primary,
122 + color: Theme.of(context).colorScheme.onPrimary,
123 + ),
124 + )
125 + ],
126 + ),
127 + ],
128 + ),
129 + ));
130 +
131 + static Widget getButton(
132 + BuildContext context, {
133 + required String label,
134 + String? imagePath,
135 + required VoidCallback onPressed,
136 + required Color backgroundColor,
137 + required Color color,
138 + }) =>
139 + Semantics(
140 + label: label,
141 + child: OutlinedButton(
142 + onPressed: onPressed,
143 + style: OutlinedButton.styleFrom(
144 + backgroundColor: backgroundColor,
145 + side: BorderSide(
146 + color: Theme.of(context).colorScheme.outlineVariant.withAlpha(0),
147 + width: 0,
148 + ),
149 + shape:
150 + RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
151 + ),
152 + child: Container(
153 + padding: const EdgeInsets.symmetric(vertical: 12),
154 + child: Row(
155 + mainAxisAlignment: MainAxisAlignment.center,
156 + children: [
157 + if (imagePath != null) ...[
158 + Image.asset(
159 + imagePath,
160 + height: 30,
161 + width: 30,
162 + color: color,
163 + ),
164 + const SizedBox(width: 8),
165 + ],
166 + Text(
167 + label,
168 + style: Theme.of(context).textTheme.bodyMedium?.copyWith(
169 + color: color,
170 + fontWeight: FontWeight.w700,
171 + ),
172 + ),
173 + ],
174 + ),
175 + ),
176 + ),
177 + );
178 +
179 + static Widget getAssetBalanceRow(
180 + BuildContext context, {
181 + required String title,
182 + required String subtitle,
183 + required CryptoCurrency currency,
184 + bool hideSymbol = true,
185 + }) =>
186 + Row(
187 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
188 + crossAxisAlignment: CrossAxisAlignment.center,
189 + children: [
190 + Column(
191 + crossAxisAlignment: CrossAxisAlignment.start,
192 + children: [
193 + Text(
194 + title,
195 + style: Theme.of(context).textTheme.bodySmall?.copyWith(
196 + color: Theme.of(context).colorScheme.onSurfaceVariant,
197 + height: 1,
198 + ),
199 + ),
200 + SizedBox(height: 6),
201 + AutoSizeText(
202 + subtitle,
203 + style: Theme.of(context).textTheme.titleLarge?.copyWith(
204 + color: Theme.of(context).colorScheme.onSurface,
205 + fontWeight: FontWeight.w900,
206 + fontSize: 24,
207 + height: 1,
208 + ),
209 + maxLines: 1,
210 + textAlign: TextAlign.start,
211 + ),
212 + ],
213 + ),
214 + SizedBox(
215 + //width: min(MediaQuery.of(context).size.width * 0.2, 100),
216 + child: Center(
217 + child: Column(
218 + children: [
219 + CakeImageWidget(
220 + imageUrl: currency.iconPath,
221 + height: 40,
222 + width: 40,
223 + errorWidget: Container(
224 + height: 30.0,
225 + width: 30.0,
226 + child: Center(
227 + child: Text(
228 + currency.title
229 + .substring(0, min(currency.title.length, 2)),
230 + style:
231 + Theme.of(context).textTheme.bodySmall?.copyWith(
232 + fontSize: 11,
233 + color: Theme.of(context)
234 + .colorScheme
235 + .onSurfaceVariant,
236 + ),
237 + ),
238 + ),
239 + decoration: BoxDecoration(
240 + shape: BoxShape.circle,
241 + color: Theme.of(context).colorScheme.surfaceContainer,
242 + ),
243 + ),
244 + ),
245 + if (!hideSymbol) ...[
246 + const SizedBox(height: 10),
247 + Text(
248 + currency.title,
249 + style: Theme.of(context).textTheme.bodyMedium?.copyWith(
250 + fontSize: 16,
251 + fontWeight: FontWeight.w700,
252 + color: Theme.of(context).colorScheme.onSurface,
253 + height: 1,
254 + ),
255 + ),
256 + ]
257 + ],
258 + ),
259 + ),
260 + ),
261 + ],
262 + );
263 +}
lib/src/screens/integrations/deuro/widgets/savings_edit_sheet.dart new
+90
@@ -0,0 +1,90 @@
1 +import 'package:auto_size_text/auto_size_text.dart';
2 +import 'package:cake_wallet/generated/i18n.dart';
3 +import 'package:cake_wallet/src/screens/base_page.dart';
4 +import 'package:cake_wallet/src/screens/integrations/deuro/widgets/numpad.dart';
5 +import 'package:cake_wallet/src/widgets/primary_button.dart';
6 +import 'package:flutter/material.dart';
7 +
8 +class SavingEditPage extends BasePage {
9 + final bool isAdding;
10 +
11 + SavingEditPage({required this.isAdding});
12 +
13 + String get title =>
14 + isAdding ? S.current.deuro_savings_add : S.current.deuro_savings_remove;
15 +
16 + @override
17 + Widget body(BuildContext context) => _SavingsEditBody();
18 +}
19 +
20 +class _SavingsEditBody extends StatefulWidget {
21 + const _SavingsEditBody();
22 +
23 + @override
24 + State<StatefulWidget> createState() => _SavingsEditBodyState();
25 +}
26 +
27 +class _SavingsEditBodyState extends State<_SavingsEditBody> {
28 + @override
29 + void initState() {
30 + WidgetsBinding.instance
31 + .addPostFrameCallback((_) => _numpadFocusNode.requestFocus());
32 + super.initState();
33 + }
34 +
35 + @override
36 + void dispose() {
37 + _numpadFocusNode.dispose();
38 + super.dispose();
39 + }
40 +
41 + String amount = '0';
42 + final FocusNode _numpadFocusNode = FocusNode();
43 +
44 + @override
45 + Widget build(BuildContext context) => SafeArea(
46 + child: Column(children: [
47 + Expanded(
48 + child: Center(
49 + child: Padding(
50 + padding: const EdgeInsets.only(left: 26, right: 26, top: 10),
51 + child: AutoSizeText(
52 + "${amount.toString()} dEuro",
53 + maxLines: 1,
54 + maxFontSize: 60,
55 + style: Theme.of(context).textTheme.headlineMedium?.copyWith(
56 + fontWeight: FontWeight.w600,
57 + fontSize: 60,
58 + color: Theme.of(context).colorScheme.onSurface,
59 + ),
60 + textAlign: TextAlign.center,
61 + ),
62 + ),
63 + )),
64 + NumberPad(
65 + focusNode: _numpadFocusNode,
66 + onNumberPressed: (i) => setState(
67 + () => amount = amount == '0' ? i.toString() : '${amount}${i}',
68 + ),
69 + onDeletePressed: () => setState(
70 + () => amount = amount.length > 1
71 + ? amount.substring(0, amount.length - 1)
72 + : '0',
73 + ),
74 + onDecimalPressed: () =>
75 + setState(() => amount = '${amount.replaceAll('.', '')}.'),
76 + ),
77 + Padding(
78 + padding: const EdgeInsets.fromLTRB(16, 12, 16, 34),
79 + child: LoadingPrimaryButton(
80 + onPressed: () => Navigator.pop(context, amount),
81 + text: S.of(context).confirm,
82 + color: Theme.of(context).colorScheme.primary,
83 + textColor: Theme.of(context).colorScheme.onPrimary,
84 + isLoading: false,
85 + isDisabled: false,
86 + ),
87 + )
88 + ]),
89 + );
90 +}
lib/src/widgets/dashboard_card_widget.dart
+1
@@ -109,6 +109,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget {
109 ],
110 ),
111 ),
112 + Padding(padding: EdgeInsets.only(left: 10)),
113 if (image != null) image! else if (svgPicture != null) svgPicture!,
114 if (icon != null) icon!
115 ],
lib/view_model/integrations/deuro_view_model.dart new
+119
@@ -0,0 +1,119 @@
1 +import 'dart:math';
2 +
3 +import 'package:cake_wallet/core/execution_state.dart';
4 +import 'package:cake_wallet/ethereum/ethereum.dart';
5 +import 'package:cake_wallet/store/app_store.dart';
6 +import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
7 +import 'package:cw_core/pending_transaction.dart';
8 +import 'package:cw_core/wallet_type.dart';
9 +import 'package:mobx/mobx.dart';
10 +
11 +part 'deuro_view_model.g.dart';
12 +
13 +class DEuroViewModel = DEuroViewModelBase with _$DEuroViewModel;
14 +
15 +abstract class DEuroViewModelBase with Store {
16 + final AppStore _appStore;
17 +
18 + DEuroViewModelBase(this._appStore) {
19 + reloadInterestRate();
20 + reloadSavingsUserData();
21 + }
22 +
23 + @observable
24 + String savingsBalance = '0.00';
25 +
26 + @observable
27 + ExecutionState state = InitialExecutionState();
28 +
29 + @observable
30 + String interestRate = '0';
31 +
32 + @observable
33 + String accruedInterest = '0.00';
34 +
35 + @observable
36 + BigInt approvedTokens = BigInt.zero;
37 +
38 + @computed
39 + bool get isEnabled => approvedTokens > BigInt.zero;
40 +
41 + @observable
42 + PendingTransaction? transaction = null;
43 +
44 + @observable
45 + PendingTransaction? approvalTransaction = null;
46 +
47 + @action
48 + Future<void> reloadSavingsUserData() async {
49 + final savingsBalanceRaw =
50 + ethereum!.getDEuroSavingsBalance(_appStore.wallet!);
51 + final accruedInterestRaw =
52 + ethereum!.getDEuroAccruedInterest(_appStore.wallet!);
53 +
54 + approvedTokens = await ethereum!.getDEuroSavingsApproved(_appStore.wallet!);
55 +
56 + savingsBalance = ethereum!
57 + .formatterEthereumAmountToDouble(amount: await savingsBalanceRaw)
58 + .toStringAsFixed(6);
59 + accruedInterest = ethereum!
60 + .formatterEthereumAmountToDouble(amount: await accruedInterestRaw)
61 + .toStringAsFixed(6);
62 + }
63 +
64 + @action
65 + Future<void> reloadInterestRate() async {
66 + final interestRateRaw =
67 + await ethereum!.getDEuroInterestRate(_appStore.wallet!);
68 +
69 + interestRate = (interestRateRaw / BigInt.from(10000)).toString();
70 + }
71 +
72 + @action
73 + Future<void> prepareApproval() async {
74 + final priority = _appStore.settingsStore.priority[WalletType.ethereum]!;
75 + approvalTransaction =
76 + await ethereum!.enableDEuroSaving(_appStore.wallet!, priority);
77 + }
78 +
79 + @action
80 + Future<void> prepareSavingsEdit(String amountRaw, bool isAdding) async {
81 + final amount = BigInt.from(num.parse(amountRaw) * pow(10, 18));
82 + final priority = _appStore.settingsStore.priority[WalletType.ethereum]!;
83 + transaction = await (isAdding
84 + ? ethereum!.addDEuroSaving(_appStore.wallet!, amount, priority)
85 + : ethereum!.removeDEuroSaving(_appStore.wallet!, amount, priority));
86 + }
87 +
88 + Future<void> prepareCollectInterest() =>
89 + prepareSavingsEdit(accruedInterest, false);
90 +
91 + @action
92 + Future<void> commitTransaction() async {
93 + if (transaction != null) {
94 + state = TransactionCommitting();
95 + await transaction!.commit();
96 + transaction = null;
97 + reloadSavingsUserData();
98 + state = TransactionCommitted();
99 + }
100 + }
101 +
102 + @action
103 + Future<void> commitApprovalTransaction() async {
104 + if (approvalTransaction != null) {
105 + state = TransactionCommitting();
106 + await approvalTransaction!.commit();
107 + approvalTransaction = null;
108 + reloadSavingsUserData();
109 + state = TransactionCommitted();
110 + }
111 + }
112 +
113 + @action
114 + void dismissTransaction() {
115 + transaction == null;
116 + approvalTransaction = null;
117 + state = InitialExecutionState();
118 + }
119 +}
res/values/strings_ar.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "تحديث APK",
57 "approve": "ﺪﻤﺘﻌﻳ",
58 "approve_request": "الموافقة على الطلب",
59 + "approve_tokens": "الموافقة على الرموز",
60 "arrive_in_this_address": "سيصل ${currency} ${tag}إلى هذا العنوان",
61 "ascending": "تصاعدي",
62 "ask_each_time": "اسأل في كل مرة",
@@ -244,6 +245,15 @@
245 "descending": "النزول",
246 "description": "ﻒﺻﻭ",
247 "destination_tag": "علامة الوجهة:",
248 + "deuro_collect_interest": "يجمع",
249 + "deuro_savings": "الادخار ديورو",
250 + "deuro_savings_add": "إيداع",
251 + "deuro_savings_balance": "توازن الادخار",
252 + "deuro_savings_collect_interest": "جمع الاهتمام",
253 + "deuro_savings_remove": "ينسحب",
254 + "deuro_savings_set_approval": "تعيين الموافقة",
255 + "deuro_savings_subtitle": "كسب ما يصل إلى 10 ٪ فائدة على مقتنيات Deuro Stablecoin",
256 + "deuro_tx_commited_content": "قد يستغرق الأمر بضع ثوانٍ حتى يتم تأكيد المعاملة وينعكس على الشاشة",
257 "device_is_signing": "الجهاز يوقع",
258 "dfx_option_description": "شراء التشفير مع EUR & CHF. لعملاء البيع بالتجزئة والشركات في أوروبا",
259 "didnt_get_code": "لم تحصل على رمز؟",
res/values/strings_bg.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK ъпдейт",
57 "approve": "Одобряване",
58 "approve_request": "Одобрете искане",
59 + "approve_tokens": "Одобрете жетоните",
60 "arrive_in_this_address": "${currency} ${tag}ще отидат на този адрес",
61 "ascending": "Възходящ",
62 "ask_each_time": "Питайте всеки път",
@@ -244,6 +245,15 @@
245 "descending": "Низходящ",
246 "description": "Описание",
247 "destination_tag": "Destination tag:",
248 + "deuro_collect_interest": "Събиране",
249 + "deuro_savings": "Спестявания на Деуро",
250 + "deuro_savings_add": "Депозит",
251 + "deuro_savings_balance": "Спестотен баланс",
252 + "deuro_savings_collect_interest": "Събиране на интерес",
253 + "deuro_savings_remove": "Оттегляне",
254 + "deuro_savings_set_approval": "Задайте одобрение",
255 + "deuro_savings_subtitle": "Печелете до 10% лихва за вашите Deuro Stablecoin Holdings",
256 + "deuro_tx_commited_content": "Може да отнеме няколко секунди, за да може транзакцията да се потвърди и да бъде отразена на екрана",
257 "device_is_signing": "Устройството подписва",
258 "dfx_option_description": "Купете криптовалута с Eur & CHF. За търговски и корпоративни клиенти в Европа",
259 "didnt_get_code": "Не получихте код?",
res/values/strings_cs.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "aktualizace APK",
57 "approve": "Schvalovat",
58 "approve_request": "Schválit žádost",
59 + "approve_tokens": "Schválit tokeny",
60 "arrive_in_this_address": "${currency} ${tag}přijde na tuto adresu",
61 "ascending": "Vzestupné",
62 "ask_each_time": "Zeptejte se pokaždé",
@@ -244,6 +245,15 @@
245 "descending": "Klesající",
246 "description": "Popis",
247 "destination_tag": "Destination Tag:",
248 + "deuro_collect_interest": "Sbírat",
249 + "deuro_savings": "dEuro úspory",
250 + "deuro_savings_add": "Vklad",
251 + "deuro_savings_balance": "Úspora zůstatek",
252 + "deuro_savings_collect_interest": "Sbírat zájem",
253 + "deuro_savings_remove": "Odstoupit",
254 + "deuro_savings_set_approval": "Stanovit schválení",
255 + "deuro_savings_subtitle": "Získejte až 10% úrok z vašeho Deuro Stablecoin Holdings",
256 + "deuro_tx_commited_content": "Transakce může trvat několik sekund, aby se potvrdila a odrážela se na obrazovce",
257 "device_is_signing": "Zařízení se podpisu",
258 "dfx_option_description": "Koupit krypto s EUR & CHF. Pro maloobchodní a firemní zákazníky v Evropě",
259 "didnt_get_code": "Nepřišel Vám kód?",
res/values/strings_de.arb
+12 -2
@@ -56,6 +56,7 @@
56 "apk_update": "APK-Update",
57 "approve": "Genehmigen",
58 "approve_request": "Anfrage genehmigen",
59 + "approve_tokens": "Token genehmigen",
60 "arrive_in_this_address": "${currency} ${tag} wird an dieser Adresse ankommen",
61 "ascending": "Aufsteigend",
62 "ask_each_time": "Jedes Mal fragen",
@@ -206,7 +207,7 @@
207 "copy": "Kopieren",
208 "copy_address": "Adresse kopieren",
209 "copy_id": "ID kopieren",
209 - "copy_payjoin_address": "Kopieren Sie Payjoin -Adresse",
210 + "copy_payjoin_address": "Kopieren Sie Payjoin-Adresse",
211 "copy_payjoin_url": "Payjoin URL kopieren",
212 "copyWalletConnectLink": "Kopieren Sie den WalletConnect-Link von dApp und fügen Sie ihn hier ein",
213 "corrupted_seed_notice": "Die Dateien für diese Wallet sind beschädigt und können nicht geöffnet werden. Bitte sehen Sie sich die Seeds an, speichern Sie sie und stellen Sie die Wallet wieder her.\n\nWenn der Wert leer ist, konnte der Seed nicht korrekt wiederhergestellt werden.",
@@ -244,6 +245,15 @@
245 "descending": "Absteigend",
246 "description": "Beschreibung",
247 "destination_tag": "Ziel-Tag:",
248 + "deuro_collect_interest": "Auszahlen",
249 + "deuro_savings": "dEuro-Savings",
250 + "deuro_savings_add": "Einzahlen",
251 + "deuro_savings_balance": "Sparguthaben",
252 + "deuro_savings_collect_interest": "Interesse sammeln",
253 + "deuro_savings_remove": "Auszahlen",
254 + "deuro_savings_set_approval": "Genehmigung festlegen",
255 + "deuro_savings_subtitle": "Verdienen Sie bis zu 10% Zinsen für Ihre dEuro Stablecoin Holdings",
256 + "deuro_tx_commited_content": "Es kann ein paar Sekunden dauern, bis die Transaktion bestätigt und auf dem Bildschirm angezeigt",
257 "device_is_signing": "Das Gerät unterschreibt",
258 "dfx_option_description": "Kaufen Sie Krypto mit EUR & CHF. Für Einzelhandel und Unternehmenskunden in Europa",
259 "didnt_get_code": "Kein Code?",
@@ -1098,4 +1108,4 @@
1108 "you_will_send": "Konvertieren von",
1109 "youCanGoBackToYourDapp": "Sie können jetzt zu Ihrem Dapp zurückkehren",
1110 "yy": "YY"
1101 -}
\ No newline at end of file
1111 +}
res/values/strings_en.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK update",
57 "approve": "Approve",
58 "approve_request": "Approve Request",
59 + "approve_tokens": "Approve tokens",
60 "arrive_in_this_address": "${currency} ${tag}will arrive in this address",
61 "ascending": "Ascending",
62 "ask_each_time": "Ask each time",
@@ -244,6 +245,15 @@
245 "descending": "Descending",
246 "description": "Description",
247 "destination_tag": "Destination tag:",
248 + "deuro_collect_interest": "Collect",
249 + "deuro_savings": "dEuro Savings",
250 + "deuro_savings_add": "Deposit",
251 + "deuro_savings_balance": "Savings Balance",
252 + "deuro_savings_collect_interest": "Collect interest",
253 + "deuro_savings_remove": "Withdraw",
254 + "deuro_savings_set_approval": "Set approval",
255 + "deuro_savings_subtitle": "Earn up to 10% interest on your dEuro Stablecoin holdings",
256 + "deuro_tx_commited_content": "It might take a couple of seconds for the transaction to confirm and be reflected on screen",
257 "device_is_signing": "Device is signing",
258 "dfx_option_description": "Buy crypto with EUR & CHF. For retail and corporate customers in Europe",
259 "didnt_get_code": "Didn't get code?",
res/values/strings_es.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "Actualización de APK",
57 "approve": "Aprobar",
58 "approve_request": "Aprobar la solicitud",
59 + "approve_tokens": "Aprobar tokens",
60 "arrive_in_this_address": "${currency} ${tag}llegará a esta dirección",
61 "ascending": "Ascendente",
62 "ask_each_time": "Pregunta cada vez",
@@ -244,6 +245,15 @@
245 "descending": "Descendente",
246 "description": "Descripción",
247 "destination_tag": "Etiqueta de destino:",
248 + "deuro_collect_interest": "Recolectar",
249 + "deuro_savings": "ahorros de deuro",
250 + "deuro_savings_add": "Depósito",
251 + "deuro_savings_balance": "Saldo de ahorro",
252 + "deuro_savings_collect_interest": "Cobrar interés",
253 + "deuro_savings_remove": "Retirar",
254 + "deuro_savings_set_approval": "Establecer aprobación",
255 + "deuro_savings_subtitle": "Gane hasta un 10% de interés en sus Holdings de Deuro Stablecoin",
256 + "deuro_tx_commited_content": "La transacción puede tardar un par de segundos en confirmar y reflejarse en la pantalla",
257 "device_is_signing": "El dispositivo está firmando",
258 "dfx_option_description": "Compre cripto con EUR y CHF. Para clientes minoristas y corporativos en Europa",
259 "didnt_get_code": "¿No recibiste el código?",
res/values/strings_fr.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "Mise à jour d'APK",
57 "approve": "Approuver",
58 "approve_request": "Approuver la demande",
59 + "approve_tokens": "Approuver les jetons",
60 "arrive_in_this_address": "${currency} ${tag}arrivera à cette adresse",
61 "ascending": "Ascendant",
62 "ask_each_time": "Demander à chaque fois",
@@ -244,6 +245,15 @@
245 "descending": "Descendant",
246 "description": "Description",
247 "destination_tag": "Tag de destination :",
248 + "deuro_collect_interest": "Collecter",
249 + "deuro_savings": "Économies de deuro",
250 + "deuro_savings_add": "Dépôt",
251 + "deuro_savings_balance": "Solde d'épargne",
252 + "deuro_savings_collect_interest": "Percevoir l'intérêt",
253 + "deuro_savings_remove": "Retirer",
254 + "deuro_savings_set_approval": "Établir l'approbation",
255 + "deuro_savings_subtitle": "Gagnez jusqu'à 10% d'intérêt sur vos avoirs de Deuro Stablecoin",
256 + "deuro_tx_commited_content": "Il pourrait prendre quelques secondes pour que la transaction confirme et soit reflétée à l'écran",
257 "device_is_signing": "L'appareil signale",
258 "dfx_option_description": "Achetez de la crypto avec EUR & CHF. Pour les clients de la vente au détail et des entreprises en Europe",
259 "didnt_get_code": "Vous n'avez pas reçu le code ?",
res/values/strings_ha.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "apk sabunta",
57 "approve": "Amincewa",
58 "approve_request": "Amince da bukata",
59 + "approve_tokens": "Amince da Alamu",
60 "arrive_in_this_address": "${currency} ${tag} zai je wurin wannan adireshi",
61 "ascending": "Hau",
62 "ask_each_time": "Tambaya kowane lokaci",
@@ -244,6 +245,15 @@
245 "descending": "Saukowa",
246 "description": "Bayani",
247 "destination_tag": "Tambarin makoma:",
248 + "deuro_collect_interest": "Tara",
249 + "deuro_savings": "deuro tanadi",
250 + "deuro_savings_add": "Yi ajiya",
251 + "deuro_savings_balance": "Ma'auni",
252 + "deuro_savings_collect_interest": "Tattara amfani da sha'awa",
253 + "deuro_savings_remove": "Janye",
254 + "deuro_savings_set_approval": "Saita yarda",
255 + "deuro_savings_subtitle": "Sami har zuwa 10% sha'awa a kan Deuro Stovecoin Rike",
256 + "deuro_tx_commited_content": "Yana iya ɗaukar wasu secondsan seconds don ma'amala don tabbatarwa kuma a nuna shi a allon",
257 "device_is_signing": "Na'urar tana shiga",
258 "dfx_option_description": "Buy crypto tare da Eur & Chf. Don Retail da abokan ciniki na kamfanoni a Turai",
259 "didnt_get_code": "Ba a samun code?",
res/values/strings_hi.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK अद्यतन",
57 "approve": "मंज़ूरी देना",
58 "approve_request": "अनुरोध को स्वीकृत करें",
59 + "approve_tokens": "टोकन को मंजूरी देना",
60 "arrive_in_this_address": "${currency} ${tag}इस पते पर पहुंचेंगे",
61 "ascending": "आरोही",
62 "ask_each_time": "हर बार पूछें",
@@ -244,6 +245,15 @@
245 "descending": "अवरोही",
246 "description": "विवरण",
247 "destination_tag": "गंतव्य टैग:",
248 + "deuro_collect_interest": "इकट्ठा करना",
249 + "deuro_savings": "देउरो बचत",
250 + "deuro_savings_add": "जमा",
251 + "deuro_savings_balance": "बचत शेष",
252 + "deuro_savings_collect_interest": "ब्याज इकट्ठा करना",
253 + "deuro_savings_remove": "निकालना",
254 + "deuro_savings_set_approval": "अनुमोदन निर्धारित करना",
255 + "deuro_savings_subtitle": "अपने Deuro Stablecoin होल्डिंग्स पर 10% ब्याज कमाएँ",
256 + "deuro_tx_commited_content": "लेन -देन की पुष्टि करने और स्क्रीन पर प्रतिबिंबित होने के लिए कुछ सेकंड लग सकते हैं",
257 "device_is_signing": "उपकरण हस्ताक्षर कर रहा है",
258 "dfx_option_description": "EUR और CHF के साथ क्रिप्टो खरीदें। यूरोप में खुदरा और कॉर्पोरेट ग्राहकों के लिए",
259 "didnt_get_code": "कोड नहीं मिला?",
res/values/strings_hr.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK ažuriranje",
57 "approve": "Odobriti",
58 "approve_request": "Odobriti zahtjev",
59 + "approve_tokens": "Odobriti tokene",
60 "arrive_in_this_address": "${currency} ${tag}će stići na ovu adresu",
61 "ascending": "Uzlazni",
62 "ask_each_time": "Pitajte svaki put",
@@ -244,6 +245,15 @@
245 "descending": "Silazni",
246 "description": "Opis",
247 "destination_tag": "Odredišna oznaka:",
248 + "deuro_collect_interest": "Prikupiti",
249 + "deuro_savings": "deuro ušteda",
250 + "deuro_savings_add": "Depozit",
251 + "deuro_savings_balance": "Ravnoteža uštede",
252 + "deuro_savings_collect_interest": "Prikupiti interes",
253 + "deuro_savings_remove": "Povući",
254 + "deuro_savings_set_approval": "Odrediti odobrenje",
255 + "deuro_savings_subtitle": "Zaradite do 10% kamate na svoje Deuro Stablecoin Holdings",
256 + "deuro_tx_commited_content": "Možda će trebati nekoliko sekundi da se transakcija potvrdi i odrazi na zaslonu",
257 "device_is_signing": "Uređaj se potpisuje",
258 "dfx_option_description": "Kupite kriptovalute s Eur & CHF. Za maloprodajne i korporativne kupce u Europi",
259 "didnt_get_code": "Ne dobivate kod?",
res/values/strings_hy.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK թարմացում",
57 "approve": "Հաստատել",
58 "approve_request": "Հաստատում է հայցը",
59 + "approve_tokens": "Հաստատում է նշանները",
60 "arrive_in_this_address": "${currency} ${tag}կժամանի այս հասցեում",
61 "ascending": "Աճող",
62 "ask_each_time": "Հարցնել ամեն անգամ",
@@ -244,6 +245,15 @@
245 "descending": "Նվազող",
246 "description": "Նկարագրություն",
247 "destination_tag": "Նպատակակետի պիտակ:",
248 + "deuro_collect_interest": "Հավաքել",
249 + "deuro_savings": "dEuro խնայողություններ",
250 + "deuro_savings_add": "Ավանդ",
251 + "deuro_savings_balance": "Խնայողական հավասարակշռություն",
252 + "deuro_savings_collect_interest": "Հավաքեք հետաքրքրություն",
253 + "deuro_savings_remove": "Հեռացնել",
254 + "deuro_savings_set_approval": "Սահմանել հաստատում",
255 + "deuro_savings_subtitle": "Վաստակեք մինչեւ 10% տոկոսադրույքներ ձեր Deuro Stablecoin Holdings- ի համար",
256 + "deuro_tx_commited_content": "Գործարքի հաստատման եւ արտացոլվելու համար գործարքի համար կարող է տեւել մի քանի վայրկյան",
257 "device_is_signing": "Սարքը ստորագրում է",
258 "dfx_option_description": "Գնեք կրիպտոարժույթ EUR և CHF: Կորպորատիվ և մանրածախ հաճախորդների համար Եվրոպայում",
259 "didnt_get_code": "Չեք ստացել կոդը?",
res/values/strings_id.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "Pembaruan APK",
57 "approve": "Menyetujui",
58 "approve_request": "Menyetujui permintaan",
59 + "approve_tokens": "Menyetujui token",
60 "arrive_in_this_address": "${currency} ${tag} akan tiba di alamat ini",
61 "ascending": "Naik",
62 "ask_each_time": "Tanyakan setiap kali",
@@ -244,6 +245,15 @@
245 "descending": "Menurun",
246 "description": "Keterangan",
247 "destination_tag": "Tag tujuan:",
248 + "deuro_collect_interest": "Mengumpulkan",
249 + "deuro_savings": "Tabungan dEuro",
250 + "deuro_savings_add": "Deposito",
251 + "deuro_savings_balance": "Keseimbangan tabungan",
252 + "deuro_savings_collect_interest": "Mengumpulkan minat",
253 + "deuro_savings_remove": "Menarik",
254 + "deuro_savings_set_approval": "Tetapkan persetujuan",
255 + "deuro_savings_subtitle": "Hasilkan hingga 10% bunga di Deuro Stablecoin Holdings Anda",
256 + "deuro_tx_commited_content": "Mungkin butuh beberapa detik untuk transaksi untuk mengkonfirmasi dan direfleksikan di layar",
257 "device_is_signing": "Perangkat sedang menandatangani",
258 "dfx_option_description": "Beli crypto dengan EUR & CHF. Untuk pelanggan ritel dan perusahaan di Eropa",
259 "didnt_get_code": "Tidak mendapatkan kode?",
res/values/strings_it.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "Aggiornamento APK",
57 "approve": "Approvare",
58 "approve_request": "Approvare la richiesta",
59 + "approve_tokens": "Approvare i token",
60 "arrive_in_this_address": "${currency} ${tag}arriverà a questo indirizzo",
61 "ascending": "Ascendente",
62 "ask_each_time": "Chiedi ogni volta",
@@ -244,6 +245,15 @@
245 "descending": "Discendente",
246 "description": "Descrizione",
247 "destination_tag": "Tag destinazione:",
248 + "deuro_collect_interest": "Raccogliere",
249 + "deuro_savings": "Risparmio di dEuro",
250 + "deuro_savings_add": "Depositare",
251 + "deuro_savings_balance": "Saldo di risparmio",
252 + "deuro_savings_collect_interest": "Raccogliere interesse",
253 + "deuro_savings_remove": "Ritirare",
254 + "deuro_savings_set_approval": "Impostare l'approvazione",
255 + "deuro_savings_subtitle": "Guadagna fino al 10% di interesse su Deuro StableCoin Holdings",
256 + "deuro_tx_commited_content": "Potrebbero essere necessari un paio di secondi per confermare la transazione ed essere riflessa sullo schermo",
257 "device_is_signing": "Il dispositivo sta firmando",
258 "dfx_option_description": "Acquista Crypto con EUR & CHF. Per i clienti al dettaglio e aziendali in Europa",
259 "didnt_get_code": "Non hai ricevuto il codice?",
res/values/strings_ja.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APKアップデート",
57 "approve": "承認する",
58 "approve_request": "リクエストを承認します",
59 + "approve_tokens": "トークンを承認します",
60 "arrive_in_this_address": "${currency} ${tag}はこの住所に到着します",
61 "ascending": "上昇",
62 "ask_each_time": "毎回尋ねてください",
@@ -244,6 +245,15 @@
245 "descending": "下降",
246 "description": "説明",
247 "destination_tag": "宛先タグ:",
248 + "deuro_collect_interest": "集める",
249 + "deuro_savings": "dEuro Savings",
250 + "deuro_savings_add": "デポジット",
251 + "deuro_savings_balance": "貯蓄バランス",
252 + "deuro_savings_collect_interest": "興味を集めます",
253 + "deuro_savings_remove": "撤回する",
254 + "deuro_savings_set_approval": "承認を設定します",
255 + "deuro_savings_subtitle": "Deuro Stablecoin Holdingsで最大10%の利息を稼ぐ",
256 + "deuro_tx_commited_content": "トランザクションが確認され、画面に反映されるまでに数秒かかる場合があります",
257 "device_is_signing": "デバイスが署名しています",
258 "dfx_option_description": "EUR&CHFで暗号を購入します。ヨーロッパの小売および企業の顧客向け",
259 "didnt_get_code": "コードを取得しませんか?",
res/values/strings_ko.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK 업데이트",
57 "approve": "승인",
58 "approve_request": "요청 승인",
59 + "approve_tokens": "토큰을 승인합니다",
60 "arrive_in_this_address": "${currency} ${tag}이(가) 이 주소로 도착합니다",
61 "ascending": "오름차순",
62 "ask_each_time": "매번 묻기",
@@ -244,6 +245,15 @@
245 "descending": "내림차순",
246 "description": "설명",
247 "destination_tag": "목적지 태그:",
248 + "deuro_collect_interest": "모으다",
249 + "deuro_savings": "도로 저축",
250 + "deuro_savings_add": "보증금",
251 + "deuro_savings_balance": "저축 잔고",
252 + "deuro_savings_collect_interest": "관심을 모으십시오",
253 + "deuro_savings_remove": "철회하다",
254 + "deuro_savings_set_approval": "승인을 설정하십시오",
255 + "deuro_savings_subtitle": "Deuro Stablecoin Holdings에 최대 10%의이자를 받으십시오.",
256 + "deuro_tx_commited_content": "트랜잭션이 확인하고 화면에 반영되는 데 몇 초가 걸릴 수 있습니다.",
257 "device_is_signing": "장치가 서명 중입니다",
258 "dfx_option_description": "EUR 및 CHF로 암호화폐 구매. 유럽의 개인 및 기업 고객 대상",
259 "didnt_get_code": "코드를 받지 못했나요?",
res/values/strings_my.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK အပ်ဒိတ်",
57 "approve": "လက်မခံပါ။",
58 "approve_request": "တောင်းဆိုမှုကိုအတည်ပြု",
59 + "approve_tokens": "တိုကင်အတည်ပြု",
60 "arrive_in_this_address": "${currency} ${tag}ဤလိပ်စာသို့ ရောက်ရှိပါမည်။",
61 "ascending": "တက်",
62 "ask_each_time": "တစ်ခုချင်းစီကိုအချိန်မေးပါ",
@@ -244,6 +245,15 @@
245 "descending": "ဆင်း",
246 "description": "ဖော်ပြချက်",
247 "destination_tag": "ခရီးဆုံးအမှတ်-",
248 + "deuro_collect_interest": "စုဝေး",
249 + "deuro_savings": "dEuro ငွေစု",
250 + "deuro_savings_add": "အပ်ငေှ",
251 + "deuro_savings_balance": "ငွေစုချိန်ခွင်လျှာ",
252 + "deuro_savings_collect_interest": "အကျိုးစီးပွားစုဆောင်းပါ",
253 + "deuro_savings_remove": "ဆုတ်ခွာ",
254 + "deuro_savings_set_approval": "အတည်ပြုချက်ကိုသတ်မှတ်ပါ",
255 + "deuro_savings_subtitle": "သင်၏ Deuro Stabloin Holdings တွင် 10% အထိစိတ်ဝင်စားပါ",
256 + "deuro_tx_commited_content": "၎င်းသည်ငွေပေးငွေယူကိုအတည်ပြုရန်နှင့်မျက်နှာပြင်ပေါ်တွင်ထင်ဟပ်ရန်စက္ကန့်အနည်းငယ်ကြာနိုင်သည်",
257 "device_is_signing": "ကိရိယာလက်မှတ်ထိုးနေသည်",
258 "dfx_option_description": "Crypto ကို EUR & CHF ဖြင့် 0 ယ်ပါ။ လက်လီရောင်းဝယ်မှုနှင့်ဥရောပရှိကော်ပိုရိတ်ဖောက်သည်များအတွက်",
259 "didnt_get_code": "ကုဒ်ကို မရဘူးလား?",
res/values/strings_nl.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK-update",
57 "approve": "Goedkeuren",
58 "approve_request": "Het verzoek goedkeuren",
59 + "approve_tokens": "Tokens goedkeuren",
60 "arrive_in_this_address": "${currency} ${tag}komt aan op dit adres",
61 "ascending": "Stijgend",
62 "ask_each_time": "Vraag het elke keer",
@@ -244,6 +245,15 @@
245 "descending": "Aflopend",
246 "description": "Beschrijving",
247 "destination_tag": "Bestemmingstag:",
248 + "deuro_collect_interest": "Verzamelen",
249 + "deuro_savings": "dEuro -besparingen",
250 + "deuro_savings_add": "Borg",
251 + "deuro_savings_balance": "Spaarbalans",
252 + "deuro_savings_collect_interest": "Verzamel interesse",
253 + "deuro_savings_remove": "Terugtrekken",
254 + "deuro_savings_set_approval": "Goedkeuring instellen",
255 + "deuro_savings_subtitle": "Verdien tot 10% rente op uw Deuro Stablecoin Holdings",
256 + "deuro_tx_commited_content": "Het kan een paar seconden duren voordat de transactie wordt bevestigd en weerspiegeld op het scherm",
257 "device_is_signing": "Apparaat ondertekent",
258 "dfx_option_description": "Koop crypto met EUR & CHF. Voor retail- en zakelijke klanten in Europa",
259 "didnt_get_code": "Geen code?",
res/values/strings_pl.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "Aktualizacja APK",
57 "approve": "Zatwierdzić",
58 "approve_request": "Zatwierdzić żądanie",
59 + "approve_tokens": "Zatwierdzić tokeny",
60 "arrive_in_this_address": "${currency} ${tag}dotrze na ten adres",
61 "ascending": "Wznoszący się",
62 "ask_each_time": "Zapytaj za każdym razem",
@@ -244,6 +245,15 @@
245 "descending": "Malejąco",
246 "description": "Opis",
247 "destination_tag": "Tag docelowy:",
248 + "deuro_collect_interest": "Zbierać",
249 + "deuro_savings": "dEuro oszczędności",
250 + "deuro_savings_add": "Depozyt",
251 + "deuro_savings_balance": "Równowaga oszczędności",
252 + "deuro_savings_collect_interest": "Zbieraj zainteresowanie",
253 + "deuro_savings_remove": "Wycofać",
254 + "deuro_savings_set_approval": "Ustaw zatwierdzenie",
255 + "deuro_savings_subtitle": "Zarabiaj do 10% odsetek od Deuro Stablecoin Holdings",
256 + "deuro_tx_commited_content": "Potwierdzenie i odbicie na ekranie może potrwać kilka sekund",
257 "device_is_signing": "Urządzenie podpisuje",
258 "dfx_option_description": "Kup krypto za EUR & CHF. Dla klientów prywatnych i korporacyjnych w Europie",
259 "didnt_get_code": "Nie dostałeś kodu?",
res/values/strings_pt.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "Atualização de APK",
57 "approve": "Aprovar",
58 "approve_request": "Aprovar solicitação",
59 + "approve_tokens": "Aprovar tokens",
60 "arrive_in_this_address": "${currency} ${tag}chegará neste endereço",
61 "ascending": "Ascendente",
62 "ask_each_time": "Pergunte cada vez",
@@ -244,6 +245,15 @@
245 "descending": "descendente",
246 "description": "Descrição",
247 "destination_tag": "Tag de destino:",
248 + "deuro_collect_interest": "Coletar",
249 + "deuro_savings": "dEuro Savings",
250 + "deuro_savings_add": "Depósito",
251 + "deuro_savings_balance": "Balanço de poupança",
252 + "deuro_savings_collect_interest": "Coletar juros",
253 + "deuro_savings_remove": "Retirar",
254 + "deuro_savings_set_approval": "Defina aprovação",
255 + "deuro_savings_subtitle": "Ganhe até 10% de juros em sua Deuro Stablecoin Holdings",
256 + "deuro_tx_commited_content": "Pode levar alguns segundos para a transação confirmar e se refletir na tela",
257 "device_is_signing": "O dispositivo está assinando",
258 "dfx_option_description": "Compre criptografia com EUR & CHF. Para clientes de varejo e corporativo na Europa",
259 "didnt_get_code": "Não recebeu o código?",
res/values/strings_ru.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "Обновление APK",
57 "approve": "Утвердить",
58 "approve_request": "Утвердить запрос",
59 + "approve_tokens": "Одобрить токены",
60 "arrive_in_this_address": "${currency} ${tag}придет на этот адрес",
61 "ascending": "Восходящий",
62 "ask_each_time": "Спросите каждый раз",
@@ -244,6 +245,15 @@
245 "descending": "Нисходящий",
246 "description": "Описание",
247 "destination_tag": "Целевой тег:",
248 + "deuro_collect_interest": "Собирать",
249 + "deuro_savings": "dEuro Savings",
250 + "deuro_savings_add": "Депозитный",
251 + "deuro_savings_balance": "Сберегательный баланс",
252 + "deuro_savings_collect_interest": "Собирать интерес",
253 + "deuro_savings_remove": "Отзывать",
254 + "deuro_savings_set_approval": "Установить утверждение",
255 + "deuro_savings_subtitle": "Заработайте до 10% процентов на ваших Deuro Stablecoin Holdings",
256 + "deuro_tx_commited_content": "Чтобы подтвердить, может потребоваться пару секунд, чтобы подтвердить и быть отраженным на экране",
257 "device_is_signing": "Устройство подписывает",
258 "dfx_option_description": "Купить крипто с Eur & CHF. Для розничных и корпоративных клиентов в Европе",
259 "didnt_get_code": "Не получить код?",
res/values/strings_th.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "ปรับปรุง APK",
57 "approve": "อนุมัติ",
58 "approve_request": "อนุมัติคำขอ",
59 + "approve_tokens": "อนุมัติโทเค็น",
60 "arrive_in_this_address": "${currency} ${tag}จะมาถึงที่อยู่นี้",
61 "ascending": "จากน้อยไปมาก",
62 "ask_each_time": "ถามทุกครั้ง",
@@ -244,6 +245,15 @@
245 "descending": "ลงมา",
246 "description": "คำอธิบาย",
247 "destination_tag": "แท็กปลายทาง:",
248 + "deuro_collect_interest": "เก็บรวบรวม",
249 + "deuro_savings": "การออมของ dEuro",
250 + "deuro_savings_add": "เงินฝาก",
251 + "deuro_savings_balance": "ยอดเงินออม",
252 + "deuro_savings_collect_interest": "เก็บดอกเบี้ย",
253 + "deuro_savings_remove": "ถอน",
254 + "deuro_savings_set_approval": "ตั้งค่าการอนุมัติ",
255 + "deuro_savings_subtitle": "รับดอกเบี้ยมากถึง 10% สำหรับ Deuro Stablecoin Holdings ของคุณ",
256 + "deuro_tx_commited_content": "อาจใช้เวลาสองสามวินาทีในการทำธุรกรรมเพื่อยืนยันและสะท้อนบนหน้าจอ",
257 "device_is_signing": "อุปกรณ์กำลังลงนาม",
258 "dfx_option_description": "ซื้อ crypto ด้วย Eur & CHF สำหรับลูกค้ารายย่อยและลูกค้าในยุโรป",
259 "didnt_get_code": "ไม่ได้รับรหัส?",
res/values/strings_tl.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK update",
57 "approve": "Aprubahan",
58 "approve_request": "Aprubahan ang kahilingan",
59 + "approve_tokens": "Aprubahan ang mga token",
60 "arrive_in_this_address": "Ang ${currency} ${tag} ay darating sa address na ito",
61 "ascending": "Umakyat",
62 "ask_each_time": "Magtanong sa tuwing",
@@ -244,6 +245,15 @@
245 "descending": "Pababang",
246 "description": "Paglalarawan",
247 "destination_tag": "Tag ng patutunguhan:",
248 + "deuro_collect_interest": "Mangolekta",
249 + "deuro_savings": "Pagtipid ni dEuro",
250 + "deuro_savings_add": "Deposito",
251 + "deuro_savings_balance": "Balanse sa pagtitipid",
252 + "deuro_savings_collect_interest": "Mangolekta ng interes",
253 + "deuro_savings_remove": "Umatras",
254 + "deuro_savings_set_approval": "Itakda ang pag -apruba",
255 + "deuro_savings_subtitle": "Kumita ng hanggang sa 10% na interes sa iyong mga hawak na Deuro StableCoin",
256 + "deuro_tx_commited_content": "Maaaring tumagal ng ilang segundo para sa transaksyon upang kumpirmahin at maipakita sa screen",
257 "device_is_signing": "Nag -sign ang aparato",
258 "dfx_option_description": "Bumili ng crypto kasama ang EUR & CHF. Para sa mga retail customer at corporate customer sa Europe",
259 "didnt_get_code": "Hindi nakuha ang code?",
res/values/strings_tr.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK güncellemesi",
57 "approve": "Onaylamak",
58 "approve_request": "Talebi Onaylama",
59 + "approve_tokens": "Jetonları onaylayın",
60 "arrive_in_this_address": "${currency} ${tag}bu adrese ulaşacak",
61 "ascending": "Yükselen",
62 "ask_each_time": "Her seferinde sor",
@@ -244,6 +245,15 @@
245 "descending": "Azalan",
246 "description": "Tanım",
247 "destination_tag": "Hedef Etiketi:",
248 + "deuro_collect_interest": "TOPLAMAK",
249 + "deuro_savings": "dEuro Tasarruf",
250 + "deuro_savings_add": "Yatırmak",
251 + "deuro_savings_balance": "Tasarruf Bakiyesi",
252 + "deuro_savings_collect_interest": "İlgi toplamak",
253 + "deuro_savings_remove": "Geri çekilmek",
254 + "deuro_savings_set_approval": "Onay ayarlamak",
255 + "deuro_savings_subtitle": "Deuro StableCoin Holdings'e% 10'a kadar faiz kazanın",
256 + "deuro_tx_commited_content": "İşlemin onaylaması ve ekrana yansıtılması birkaç saniye sürebilir",
257 "device_is_signing": "Cihaz imzalıyor",
258 "dfx_option_description": "Eur & chf ile kripto satın alın. Avrupa'daki perakende ve kurumsal müşteriler için",
259 "didnt_get_code": "Kod gelmedi mi?",
res/values/strings_uk.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "Оновлення APK",
57 "approve": "Затвердити",
58 "approve_request": "Запитайте запит",
59 + "approve_tokens": "Затвердити токени",
60 "arrive_in_this_address": "${currency} ${tag}надійде на цю адресу",
61 "ascending": "Висхід",
62 "ask_each_time": "Запитайте кожен раз",
@@ -244,6 +245,15 @@
245 "descending": "Низхідний",
246 "description": "опис",
247 "destination_tag": "Тег призначення:",
248 + "deuro_collect_interest": "Збирати",
249 + "deuro_savings": "заощадження dEuro",
250 + "deuro_savings_add": "Депозит",
251 + "deuro_savings_balance": "Баланс заощаджень",
252 + "deuro_savings_collect_interest": "Збирати інтерес",
253 + "deuro_savings_remove": "Відступати",
254 + "deuro_savings_set_approval": "Встановити схвалення",
255 + "deuro_savings_subtitle": "Заробляйте до 10% відсотків на ваших Holdings Deuro StableCoin",
256 + "deuro_tx_commited_content": "Це може знадобитися кілька секунд, щоб транзакція підтвердила та відображалася на екрані",
257 "device_is_signing": "Пристрій підписується",
258 "dfx_option_description": "Купуйте криптовалюту з EUR & CHF. Для роздрібних та корпоративних клієнтів у Європі",
259 "didnt_get_code": "Не отримали код?",
res/values/strings_ur.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK اپ ڈیٹ",
57 "approve": "ﻭﺮﮐ ﺭﻮﻈﻨﻣ",
58 "approve_request": "درخواست کو منظور کریں",
59 + "approve_tokens": "ٹوکن کو منظور کریں",
60 "arrive_in_this_address": "${currency} ${tag}اس پتے پر پہنچے گا۔",
61 "ascending": "چڑھنے",
62 "ask_each_time": "ہر بار پوچھیں",
@@ -244,6 +245,15 @@
245 "descending": "اترتے ہوئے",
246 "description": "ﻞﯿﺼﻔﺗ",
247 "destination_tag": "منزل کا ٹیگ:",
248 + "deuro_collect_interest": "جمع کریں",
249 + "deuro_savings": "ڈیورو کی بچت",
250 + "deuro_savings_add": "جمع کروائیں",
251 + "deuro_savings_balance": "بچت کا توازن",
252 + "deuro_savings_collect_interest": "دلچسپی جمع کریں",
253 + "deuro_savings_remove": "واپس لے لو",
254 + "deuro_savings_set_approval": "منظوری طے کریں",
255 + "deuro_savings_subtitle": "اپنے ڈیورو اسٹبل کوئن ہولڈنگز پر 10 ٪ سود حاصل کریں",
256 + "deuro_tx_commited_content": "لین دین کی تصدیق اور اسکرین پر عکاسی کرنے میں اس میں کچھ سیکنڈ لگ سکتے ہیں",
257 "device_is_signing": "ڈیوائس پر دستخط کر رہے ہیں",
258 "dfx_option_description": "یورو اور سی ایچ ایف کے ساتھ کرپٹو خریدیں۔ یورپ میں خوردہ اور کارپوریٹ صارفین کے لئے",
259 "didnt_get_code": "کوڈ نہیں ملتا؟",
res/values/strings_vi.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "Cập nhật APK",
57 "approve": "Phê duyệt",
58 "approve_request": "Phê duyệt yêu cầu",
59 + "approve_tokens": "Phê duyệt mã thông báo",
60 "arrive_in_this_address": "${currency} ${tag} sẽ đến địa chỉ này",
61 "ascending": "Tăng dần",
62 "ask_each_time": "Hỏi mỗi lần",
@@ -243,6 +244,15 @@
244 "descending": "Giảm dần",
245 "description": "Mô tả",
246 "destination_tag": "Thẻ đích:",
247 + "deuro_collect_interest": "Sưu tầm",
248 + "deuro_savings": "Tiết kiệm dEuro",
249 + "deuro_savings_add": "Tiền gửi",
250 + "deuro_savings_balance": "Số dư tiết kiệm",
251 + "deuro_savings_collect_interest": "Thu tiền lãi",
252 + "deuro_savings_remove": "Rút",
253 + "deuro_savings_set_approval": "Đặt phê duyệt",
254 + "deuro_savings_subtitle": "Kiếm tới 10% tiền lãi cho Deuro Storcoin Holdings của bạn",
255 + "deuro_tx_commited_content": "Có thể mất vài giây để giao dịch xác nhận và được phản ánh trên màn hình",
256 "device_is_signing": "Thiết bị đang ký",
257 "dfx_option_description": "Mua tiền điện tử bằng EUR & CHF. Dành cho khách hàng bán lẻ và doanh nghiệp tại Châu Âu",
258 "didnt_get_code": "Không nhận được mã?",
res/values/strings_yo.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "Àtúnse áàpù títun wà",
57 "approve": "Fi ọwọ si",
58 "approve_request": "IKILỌ RẸ",
59 + "approve_tokens": "Ṣe fọwọsi awọn àmi",
60 "arrive_in_this_address": "${currency} ${tag} máa dé sí àdírẹ́sì yìí",
61 "ascending": "Goke",
62 "ask_each_time": "Beere lọwọ kọọkan",
@@ -244,6 +245,15 @@
245 "descending": "Sọkalẹ",
246 "description": "Apejuwe",
247 "destination_tag": "Orúkọ tí ìbí tó a ránṣẹ́ sí:",
248 + "deuro_collect_interest": "Kojọ",
249 + "deuro_savings": "dEuro Awọn ifowopamọ",
250 + "deuro_savings_add": "Owo ifipamọ",
251 + "deuro_savings_balance": "Iwontunws.funfun ifowopamọ",
252 + "deuro_savings_collect_interest": "Gba iwulo",
253 + "deuro_savings_remove": "Yọkuro",
254 + "deuro_savings_set_approval": "Ṣeto ifọwọsi",
255 + "deuro_savings_subtitle": "Jo'gun to 10% iwulo lori awọn idaduro Duroblockoin rẹ",
256 + "deuro_tx_commited_content": "O le gba tọkọtaya kan ti awọn aaya fun idunadura lati jẹrisi ati ṣe afihan loju iboju",
257 "device_is_signing": "Ẹrọ n forukọsilẹ",
258 "dfx_option_description": "Ra Crypto pẹlu EUR & CHF. Fun soobu ati awọn alabara ile-iṣẹ ni Yuroopu",
259 "didnt_get_code": "Ko gba koodu?",
res/values/strings_zh.arb
+10
@@ -56,6 +56,7 @@
56 "apk_update": "APK更新",
57 "approve": "批准",
58 "approve_request": "批准请求",
59 + "approve_tokens": "批准令牌",
60 "arrive_in_this_address": "${currency} ${tag}将到达此地址",
61 "ascending": "上升",
62 "ask_each_time": "每次问",
@@ -244,6 +245,15 @@
245 "descending": "下降",
246 "description": "描述",
247 "destination_tag": "目标Tag:",
248 + "deuro_collect_interest": "收集",
249 + "deuro_savings": "dEuro储蓄",
250 + "deuro_savings_add": "订金",
251 + "deuro_savings_balance": "储蓄平衡",
252 + "deuro_savings_collect_interest": "收集兴趣",
253 + "deuro_savings_remove": "提取",
254 + "deuro_savings_set_approval": "设定批准",
255 + "deuro_savings_subtitle": "您的Deuro Stablecoin Holdings最多可赚取10%的利息",
256 + "deuro_tx_commited_content": "交易可能需要几秒钟才能确认并在屏幕上反射",
257 "device_is_signing": "设备正在签名",
258 "dfx_option_description": "用Eur&Chf购买加密货币。对于欧洲的零售和企业客户",
259 "didnt_get_code": "没有获取代码?",
tool/configure.dart
+15
@@ -670,6 +670,7 @@ import 'package:cw_core/crypto_currency.dart';
670 import 'package:cw_core/erc20_token.dart';
671 import 'package:cw_core/hardware/hardware_account_data.dart';
672 import 'package:cw_core/output_info.dart';
673 +import 'package:cw_core/pending_transaction.dart';
674 import 'package:cw_core/transaction_info.dart';
675 import 'package:cw_core/transaction_priority.dart';
676 import 'package:cw_core/wallet_base.dart';
@@ -697,6 +698,7 @@ import 'package:cw_ethereum/ethereum_client.dart';
698 import 'package:cw_ethereum/ethereum_wallet.dart';
699 import 'package:cw_ethereum/ethereum_wallet_service.dart';
700 import 'package:cw_ethereum/default_ethereum_erc20_tokens.dart';
701 +import 'package:cw_ethereum/deuro/deuro_savings.dart';
702
703 import 'package:eth_sig_util/util/utils.dart';
704
@@ -744,6 +746,16 @@ abstract class Ethereum {
746 void updateEtherscanUsageState(WalletBase wallet, bool isEnabled);
747 Web3Client? getWeb3Client(WalletBase wallet);
748 String getTokenAddress(CryptoCurrency asset);
749 +
750 + Future<PendingTransaction> createTokenApproval(WalletBase wallet, BigInt amount, String spender, CryptoCurrency token, TransactionPriority priority);
751 +
752 + Future<BigInt> getDEuroSavingsBalance(WalletBase wallet);
753 + Future<BigInt> getDEuroAccruedInterest(WalletBase wallet);
754 + Future<BigInt> getDEuroInterestRate(WalletBase wallet);
755 + Future<BigInt> getDEuroSavingsApproved(WalletBase wallet);
756 + Future<PendingTransaction> addDEuroSaving(WalletBase wallet, BigInt amount, TransactionPriority priority);
757 + Future<PendingTransaction> removeDEuroSaving(WalletBase wallet, BigInt amount, TransactionPriority priority);
758 + Future<PendingTransaction> enableDEuroSaving(WalletBase wallet, TransactionPriority priority);
759
760 void setLedgerConnection(WalletBase wallet, ledger.LedgerConnection connection);
761 Future<List<HardwareAccountData>> getHardwareWalletAccounts(LedgerViewModel ledgerVM, {int index = 0, int limit = 5});
@@ -777,6 +789,7 @@ import 'package:cw_core/crypto_currency.dart';
789 import 'package:cw_core/erc20_token.dart';
790 import 'package:cw_core/hardware/hardware_account_data.dart';
791 import 'package:cw_core/output_info.dart';
792 +import 'package:cw_core/pending_transaction.dart';
793 import 'package:cw_core/transaction_info.dart';
794 import 'package:cw_core/transaction_priority.dart';
795 import 'package:cw_core/wallet_base.dart';
@@ -846,6 +859,8 @@ abstract class Polygon {
859 Future<void> deleteErc20Token(WalletBase wallet, CryptoCurrency token);
860 Future<void> removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token);
861 Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress);
862 +
863 + Future<PendingTransaction> createTokenApproval(WalletBase wallet, BigInt amount, String spender, CryptoCurrency token, TransactionPriority priority);
864
865 CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction);
866 void updatePolygonScanUsageState(WalletBase wallet, bool isEnabled);