dev
dart 250 lines 9.55 KB
Raw
1 import 'package:cw_core/amount/money.dart';
2 import 'package:cw_core/crypto_currency.dart';
3 import 'package:cw_evm/deuro/deuro_savings_gateway_contract.dart' as v1;
4 import 'package:cw_evm/deuro/deuro_savings_contract.dart' as v2;
5 import 'package:cw_evm/evm_chain_wallet.dart';
6 import 'package:cw_evm/contract/erc20.dart';
7 import 'package:cw_evm/evm_chain_exceptions.dart';
8 import 'package:cw_evm/evm_chain_transaction_priority.dart';
9 import 'package:cw_evm/pending_evm_chain_transaction.dart';
10 import 'package:web3dart/crypto.dart';
11 import 'package:web3dart/web3dart.dart';
12
13 const String savingsGatewayAddress = "0x073493d73258C4BEb6542e8dd3e1b2891C972303";
14 const String savingsV2Address = "0x760233b90e45d186A9A98E911B115F7F4B90d3D9";
15
16 const String dEuroAddress = "0xbA3f535bbCcCcA2A154b573Ca6c5A49BAAE0a3ea";
17 const String frontendCode = "0x00000000000000000000000000000000000000000043616b652057616c6c6574";
18
19 class DEuro {
20 final v2.Savings _savings;
21 final ERC20 _dEuro;
22 final EVMChainWallet _wallet;
23
24 DEuro(EVMChainWallet wallet)
25 : _wallet = wallet,
26 _savings = _getSavings(wallet.getWeb3Client()!),
27 _dEuro = _getDEuroToken(wallet.getWeb3Client()!);
28
29 static v2.Savings _getSavings(Web3Client client) => v2.Savings(
30 address: EthereumAddress.fromHex(savingsV2Address),
31 client: client,
32 );
33
34 static v1.SavingsGateway _getSavingsGateway(Web3Client client) => v1.SavingsGateway(
35 address: EthereumAddress.fromHex(savingsGatewayAddress),
36 client: client,
37 );
38
39 static ERC20 _getDEuroToken(Web3Client client) => ERC20(
40 address: EthereumAddress.fromHex(dEuroAddress),
41 client: client,
42 );
43
44 EthereumAddress get _address => EthereumAddress.fromHex(_wallet.walletAddresses.primaryAddress);
45
46 Future<Money> get savingsBalance async =>
47 Money((await _savings.savings(accountOwner: _address)).saved, CryptoCurrency.deuro);
48
49 Future<Money> get savingsBalanceV1 async => Money(
50 (await _getSavingsGateway(_wallet.getWeb3Client()!).savings(accountOwner: _address)).saved,
51 CryptoCurrency.deuro);
52
53 Future<Money> get accruedInterest async =>
54 Money(await _savings.accruedInterest(accountOwner: _address), CryptoCurrency.deuro);
55
56 Future<BigInt> get interestRate => _savings.currentRatePPM();
57
58 Future<BigInt> get approvedBalance => _dEuro.allowance(_address, _savings.self.address);
59
60 Future<void> _checkEthBalanceForGasFees(EVMChainTransactionPriority priority) async {
61 final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
62 final currentBalance = ethBalance.getInWei;
63 final savings = _getSavingsGateway(_wallet.getWeb3Client()!);
64
65 final gasFeesModel = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
66 amount: Money.zero(_wallet.currency),
67 contractAddress: savings.self.address.hexEip55,
68 receivingAddressHex: savings.self.address.hexEip55,
69 priority: priority,
70 data: savings.self.abi.functions[17].encodeCall([BigInt.zero, hexToBytes(frontendCode)]),
71 );
72
73 final estimatedGasFee = BigInt.from(gasFeesModel.estimatedGasFee);
74 final requiredBalance = estimatedGasFee;
75
76 if (currentBalance < requiredBalance) {
77 throw InsufficientGasFeeException(
78 requiredGasFee: requiredBalance,
79 currentBalance: currentBalance,
80 );
81 }
82 }
83
84 Future<PendingEVMChainTransaction> depositSavings(
85 BigInt amount, EVMChainTransactionPriority priority) async {
86 try {
87 await _checkEthBalanceForGasFees(priority);
88
89 final signedTransaction = await _savings.save(
90 (amount: amount, compound: true),
91 credentials: _wallet.evmChainPrivateKey,
92 );
93
94 final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
95 amount: Money.zero(_wallet.currency),
96 contractAddress: _savings.self.address.hexEip55,
97 receivingAddressHex: _savings.self.address.hexEip55,
98 priority: priority,
99 data: _savings.self.abi.functions[18].encodeCall([amount, true]),
100 );
101
102 sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
103
104 return PendingEVMChainTransaction(
105 sendTransaction: sendTransaction,
106 signedTransaction: signedTransaction,
107 fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
108 amount: Money(amount, CryptoCurrency.deuro),
109 );
110 } catch (e) {
111 if (e.toString().contains('insufficient funds for gas')) {
112 final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
113 throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
114 }
115 rethrow;
116 }
117 }
118
119 Future<PendingEVMChainTransaction> withdrawSavings(
120 BigInt amount, EVMChainTransactionPriority priority) async {
121 try {
122 await _checkEthBalanceForGasFees(priority);
123
124 final signedTransaction = await _savings.withdraw(
125 (target: _address, amount: amount),
126 credentials: _wallet.evmChainPrivateKey,
127 );
128
129 final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
130 amount: Money.zero(_wallet.currency),
131 contractAddress: _savings.self.address.hexEip55,
132 receivingAddressHex: _savings.self.address.hexEip55,
133 priority: priority,
134 data: _savings.self.abi.functions[23].encodeCall([_address, amount]),
135 );
136
137 sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
138
139 return PendingEVMChainTransaction(
140 sendTransaction: sendTransaction,
141 signedTransaction: signedTransaction,
142 fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
143 amount: Money(amount, CryptoCurrency.deuro),
144 );
145 } catch (e) {
146 if (e.toString().contains('insufficient funds for gas')) {
147 final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
148 throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
149 }
150 rethrow;
151 }
152 }
153
154 Future<PendingEVMChainTransaction> withdrawSavingsV1(EVMChainTransactionPriority priority) async {
155 try {
156 await _checkEthBalanceForGasFees(priority);
157 final withdrawAmount = await savingsBalanceV1;
158
159 // Withdraw at least a million to overflow and close the savings position
160 final amount = BigInt.parse("1000000000000000000000000");
161 final savings = _getSavingsGateway(_wallet.getWeb3Client()!);
162 final signedTransaction = await savings.withdraw(
163 (target: _address, amount: amount, frontendCode: hexToBytes(frontendCode)),
164 credentials: _wallet.evmChainPrivateKey,
165 );
166
167 final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
168 amount: Money.zero(_wallet.currency),
169 contractAddress: savings.self.address.hexEip55,
170 receivingAddressHex: savings.self.address.hexEip55,
171 priority: priority,
172 data:
173 savings.self.abi.functions[24].encodeCall([_address, amount, hexToBytes(frontendCode)]),
174 );
175
176 sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
177
178 return PendingEVMChainTransaction(
179 sendTransaction: sendTransaction,
180 signedTransaction: signedTransaction,
181 fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
182 amount: withdrawAmount,
183 );
184 } catch (e) {
185 if (e.toString().contains('insufficient funds for gas')) {
186 final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
187 throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
188 }
189 rethrow;
190 }
191 }
192
193 Future<PendingEVMChainTransaction> reinvestInterest(EVMChainTransactionPriority priority) async {
194 try {
195 await _checkEthBalanceForGasFees(priority);
196
197 final signedTransaction = await _savings.refreshBalance(
198 (owner: _address),
199 credentials: _wallet.evmChainPrivateKey,
200 );
201
202 final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
203 amount: Money.zero(_wallet.currency),
204 contractAddress: _savings.self.address.hexEip55,
205 receivingAddressHex: _savings.self.address.hexEip55,
206 priority: priority,
207 data: _savings.self.abi.functions[16].encodeCall([_address]),
208 );
209
210 sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
211
212 return PendingEVMChainTransaction(
213 sendTransaction: sendTransaction,
214 signedTransaction: signedTransaction,
215 fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
216 amount: Money.zero(CryptoCurrency.deuro),
217 );
218 } catch (e) {
219 if (e.toString().contains('insufficient funds for gas')) {
220 final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
221 throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
222 }
223 rethrow;
224 }
225 }
226
227 // Set an infinite approval to save gas in the future
228 Future<PendingEVMChainTransaction> enableSavings(EVMChainTransactionPriority priority) async {
229 try {
230 await _checkEthBalanceForGasFees(priority);
231
232 return (await _wallet.createApprovalTransaction(
233 Money(
234 BigInt.parse(
235 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
236 radix: 16,
237 ),
238 CryptoCurrency.deuro),
239 _savings.self.address.hexEip55,
240 priority,
241 )) as PendingEVMChainTransaction;
242 } catch (e) {
243 if (e.toString().contains('insufficient funds for gas')) {
244 final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
245 throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
246 }
247 rethrow;
248 }
249 }
250 }