mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
CW-596-Solana-Bug-Fixes (#1340)
* fix: Generic bug fixes across solana * fix: Remove back and forth parsing * fix: Add check to cut flow when estimated fee is higher than wallet balance * Update error message for fees exception * Remove logs --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
698c222291
commit
a9b8c03e55
13 changed files with 297 additions and 118 deletions
|
@ -82,7 +82,7 @@ abstract class EVMChainClient {
|
|||
Future<PendingEVMChainTransaction> signTransaction({
|
||||
required EthPrivateKey privateKey,
|
||||
required String toAddress,
|
||||
required String amount,
|
||||
required BigInt amount,
|
||||
required int gas,
|
||||
required EVMChainTransactionPriority priority,
|
||||
required CryptoCurrency currency,
|
||||
|
@ -103,7 +103,7 @@ abstract class EVMChainClient {
|
|||
from: privateKey.address,
|
||||
to: EthereumAddress.fromHex(toAddress),
|
||||
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
|
||||
amount: isEVMCompatibleChain ? EtherAmount.inWei(BigInt.parse(amount)) : EtherAmount.zero(),
|
||||
amount: isEVMCompatibleChain ? EtherAmount.inWei(amount) : EtherAmount.zero(),
|
||||
data: data != null ? hexToBytes(data) : null,
|
||||
);
|
||||
|
||||
|
@ -124,7 +124,7 @@ abstract class EVMChainClient {
|
|||
_sendTransaction = () async {
|
||||
await erc20.transfer(
|
||||
EthereumAddress.fromHex(toAddress),
|
||||
BigInt.parse(amount),
|
||||
amount,
|
||||
credentials: privateKey,
|
||||
transaction: transaction,
|
||||
);
|
||||
|
@ -133,7 +133,7 @@ abstract class EVMChainClient {
|
|||
|
||||
return PendingEVMChainTransaction(
|
||||
signedTransaction: signedTransaction,
|
||||
amount: amount,
|
||||
amount: amount.toString(),
|
||||
fee: BigInt.from(gas) * (await price).getInWei,
|
||||
sendTransaction: _sendTransaction,
|
||||
exponent: exponent,
|
||||
|
|
|
@ -9,3 +9,14 @@ class EVMChainTransactionCreationException implements Exception {
|
|||
@override
|
||||
String toString() => exceptionMessage;
|
||||
}
|
||||
|
||||
|
||||
class EVMChainTransactionFeesException implements Exception {
|
||||
final String exceptionMessage;
|
||||
|
||||
EVMChainTransactionFeesException()
|
||||
: exceptionMessage = 'Current balance is less than the estimated fees for this transaction.';
|
||||
|
||||
@override
|
||||
String toString() => exceptionMessage;
|
||||
}
|
||||
|
|
|
@ -234,7 +234,7 @@ abstract class EVMChainWalletBase
|
|||
final CryptoCurrency transactionCurrency =
|
||||
balance.keys.firstWhere((element) => element.title == _credentials.currency.title);
|
||||
|
||||
final _erc20Balance = balance[transactionCurrency]!;
|
||||
final erc20Balance = balance[transactionCurrency]!;
|
||||
BigInt totalAmount = BigInt.zero;
|
||||
int exponent = transactionCurrency is Erc20Token ? transactionCurrency.decimal : 18;
|
||||
num amountToEVMChainMultiplier = pow(10, exponent);
|
||||
|
@ -249,7 +249,7 @@ abstract class EVMChainWalletBase
|
|||
outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)));
|
||||
totalAmount = BigInt.from(totalOriginalAmount * amountToEVMChainMultiplier);
|
||||
|
||||
if (_erc20Balance.balance < totalAmount) {
|
||||
if (erc20Balance.balance < totalAmount) {
|
||||
throw EVMChainTransactionCreationException(transactionCurrency);
|
||||
}
|
||||
} else {
|
||||
|
@ -258,18 +258,27 @@ abstract class EVMChainWalletBase
|
|||
// then no need to subtract the fees from the amount if send all
|
||||
final BigInt allAmount;
|
||||
if (transactionCurrency is Erc20Token) {
|
||||
allAmount = _erc20Balance.balance;
|
||||
allAmount = erc20Balance.balance;
|
||||
} else {
|
||||
allAmount = _erc20Balance.balance -
|
||||
BigInt.from(calculateEstimatedFee(_credentials.priority!, null));
|
||||
}
|
||||
final totalOriginalAmount =
|
||||
EVMChainFormatter.parseEVMChainAmountToDouble(output.formattedCryptoAmount ?? 0);
|
||||
totalAmount = output.sendAll
|
||||
? allAmount
|
||||
: BigInt.from(totalOriginalAmount * amountToEVMChainMultiplier);
|
||||
final estimatedFee = BigInt.from(calculateEstimatedFee(_credentials.priority!, null));
|
||||
|
||||
if (_erc20Balance.balance < totalAmount) {
|
||||
if (estimatedFee > erc20Balance.balance) {
|
||||
throw EVMChainTransactionFeesException();
|
||||
}
|
||||
|
||||
allAmount = erc20Balance.balance - estimatedFee;
|
||||
}
|
||||
|
||||
if (output.sendAll) {
|
||||
totalAmount = allAmount;
|
||||
} else {
|
||||
final totalOriginalAmount =
|
||||
EVMChainFormatter.parseEVMChainAmountToDouble(output.formattedCryptoAmount ?? 0);
|
||||
|
||||
totalAmount = BigInt.from(totalOriginalAmount * amountToEVMChainMultiplier);
|
||||
}
|
||||
|
||||
if (erc20Balance.balance < totalAmount) {
|
||||
throw EVMChainTransactionCreationException(transactionCurrency);
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +288,7 @@ abstract class EVMChainWalletBase
|
|||
toAddress: _credentials.outputs.first.isParsedAddress
|
||||
? _credentials.outputs.first.extractedAddress!
|
||||
: _credentials.outputs.first.address,
|
||||
amount: totalAmount.toString(),
|
||||
amount: totalAmount,
|
||||
gas: _estimatedGas!,
|
||||
priority: _credentials.priority!,
|
||||
currency: transactionCurrency,
|
||||
|
|
|
@ -96,16 +96,30 @@ class SolanaWalletClient {
|
|||
return SolanaBalance(totalBalance);
|
||||
}
|
||||
|
||||
Future<double> getGasForMessage(String message) async {
|
||||
Future<double> getFeeForMessage(String message, Commitment commitment) async {
|
||||
try {
|
||||
final gasPrice = await _client!.rpcClient.getFeeForMessage(message) ?? 0;
|
||||
final fee = gasPrice / lamportsPerSol;
|
||||
final feeForMessage =
|
||||
await _client!.rpcClient.getFeeForMessage(message, commitment: commitment);
|
||||
final fee = (feeForMessage ?? 0.0) / lamportsPerSol;
|
||||
return fee;
|
||||
} catch (_) {
|
||||
return 0;
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
Future<double> getEstimatedFee(Ed25519HDKeyPair ownerKeypair) async {
|
||||
const commitment = Commitment.confirmed;
|
||||
|
||||
final message =
|
||||
_getMessageForNativeTransaction(ownerKeypair, ownerKeypair.address, lamportsPerSol);
|
||||
|
||||
final recentBlockhash = await _getRecentBlockhash(commitment);
|
||||
|
||||
final estimatedFee =
|
||||
_getFeeFromCompiledMessage(message, ownerKeypair.publicKey, recentBlockhash, commitment);
|
||||
return estimatedFee;
|
||||
}
|
||||
|
||||
/// Load the Address's transactions into the account
|
||||
Future<List<SolanaTransactionModel>> fetchTransactions(
|
||||
Ed25519HDPublicKey publicKey, {
|
||||
|
@ -257,24 +271,15 @@ class SolanaWalletClient {
|
|||
Future<PendingSolanaTransaction> signSolanaTransaction({
|
||||
required String tokenTitle,
|
||||
required int tokenDecimals,
|
||||
String? tokenMint,
|
||||
required double inputAmount,
|
||||
required String destinationAddress,
|
||||
required Ed25519HDKeyPair ownerKeypair,
|
||||
required bool isSendAll,
|
||||
String? tokenMint,
|
||||
List<String> references = const [],
|
||||
}) async {
|
||||
const commitment = Commitment.confirmed;
|
||||
|
||||
final latestBlockhash =
|
||||
await _client!.rpcClient.getLatestBlockhash(commitment: commitment).value;
|
||||
|
||||
final recentBlockhash = RecentBlockhash(
|
||||
blockhash: latestBlockhash.blockhash,
|
||||
feeCalculator: const FeeCalculator(
|
||||
lamportsPerSignature: 500,
|
||||
),
|
||||
);
|
||||
|
||||
if (tokenTitle == CryptoCurrency.sol.title) {
|
||||
final pendingNativeTokenTransaction = await _signNativeTokenTransaction(
|
||||
tokenTitle: tokenTitle,
|
||||
|
@ -282,8 +287,8 @@ class SolanaWalletClient {
|
|||
inputAmount: inputAmount,
|
||||
destinationAddress: destinationAddress,
|
||||
ownerKeypair: ownerKeypair,
|
||||
recentBlockhash: recentBlockhash,
|
||||
commitment: commitment,
|
||||
isSendAll: isSendAll,
|
||||
);
|
||||
return pendingNativeTokenTransaction;
|
||||
} else {
|
||||
|
@ -294,25 +299,29 @@ class SolanaWalletClient {
|
|||
inputAmount: inputAmount,
|
||||
destinationAddress: destinationAddress,
|
||||
ownerKeypair: ownerKeypair,
|
||||
recentBlockhash: recentBlockhash,
|
||||
commitment: commitment,
|
||||
);
|
||||
return pendingSPLTokenTransaction;
|
||||
}
|
||||
}
|
||||
|
||||
Future<PendingSolanaTransaction> _signNativeTokenTransaction({
|
||||
required String tokenTitle,
|
||||
required int tokenDecimals,
|
||||
required double inputAmount,
|
||||
required String destinationAddress,
|
||||
required Ed25519HDKeyPair ownerKeypair,
|
||||
required RecentBlockhash recentBlockhash,
|
||||
required Commitment commitment,
|
||||
}) async {
|
||||
// Convert SOL to lamport
|
||||
int lamports = (inputAmount * lamportsPerSol).toInt();
|
||||
Future<RecentBlockhash> _getRecentBlockhash(Commitment commitment) async {
|
||||
final latestBlockhash =
|
||||
await _client!.rpcClient.getLatestBlockhash(commitment: commitment).value;
|
||||
|
||||
final recentBlockhash = RecentBlockhash(
|
||||
blockhash: latestBlockhash.blockhash,
|
||||
feeCalculator: const FeeCalculator(lamportsPerSignature: 500),
|
||||
);
|
||||
|
||||
return recentBlockhash;
|
||||
}
|
||||
|
||||
Message _getMessageForNativeTransaction(
|
||||
Ed25519HDKeyPair ownerKeypair,
|
||||
String destinationAddress,
|
||||
int lamports,
|
||||
) {
|
||||
final instructions = [
|
||||
SystemInstruction.transfer(
|
||||
fundingAccount: ownerKeypair.publicKey,
|
||||
|
@ -322,21 +331,75 @@ class SolanaWalletClient {
|
|||
];
|
||||
|
||||
final message = Message(instructions: instructions);
|
||||
return message;
|
||||
}
|
||||
|
||||
Future<double> _getFeeFromCompiledMessage(
|
||||
Message message,
|
||||
Ed25519HDPublicKey feePayer,
|
||||
RecentBlockhash recentBlockhash,
|
||||
Commitment commitment,
|
||||
) async {
|
||||
final compile = message.compile(
|
||||
recentBlockhash: recentBlockhash.blockhash,
|
||||
feePayer: feePayer,
|
||||
);
|
||||
|
||||
final base64Message = base64Encode(compile.toByteArray().toList());
|
||||
|
||||
final fee = await getFeeForMessage(base64Message, commitment);
|
||||
|
||||
return fee;
|
||||
}
|
||||
|
||||
Future<PendingSolanaTransaction> _signNativeTokenTransaction({
|
||||
required String tokenTitle,
|
||||
required int tokenDecimals,
|
||||
required double inputAmount,
|
||||
required String destinationAddress,
|
||||
required Ed25519HDKeyPair ownerKeypair,
|
||||
required Commitment commitment,
|
||||
required bool isSendAll,
|
||||
}) async {
|
||||
// Convert SOL to lamport
|
||||
int lamports = (inputAmount * lamportsPerSol).toInt();
|
||||
|
||||
Message message = _getMessageForNativeTransaction(ownerKeypair, destinationAddress, lamports);
|
||||
|
||||
final signers = [ownerKeypair];
|
||||
|
||||
final signedTx = await _signTransactionInternal(
|
||||
message: message,
|
||||
signers: signers,
|
||||
commitment: commitment,
|
||||
recentBlockhash: recentBlockhash,
|
||||
);
|
||||
RecentBlockhash recentBlockhash = await _getRecentBlockhash(commitment);
|
||||
|
||||
final fee = await _getFeeFromCompiledMessage(
|
||||
message,
|
||||
recentBlockhash,
|
||||
signers.first.publicKey,
|
||||
recentBlockhash,
|
||||
commitment,
|
||||
);
|
||||
|
||||
SignedTx signedTx;
|
||||
if (isSendAll) {
|
||||
final feeInLamports = (fee * lamportsPerSol).toInt();
|
||||
final updatedLamports = lamports - feeInLamports;
|
||||
|
||||
final updatedMessage =
|
||||
_getMessageForNativeTransaction(ownerKeypair, destinationAddress, updatedLamports);
|
||||
|
||||
signedTx = await _signTransactionInternal(
|
||||
message: updatedMessage,
|
||||
signers: signers,
|
||||
commitment: commitment,
|
||||
recentBlockhash: recentBlockhash,
|
||||
);
|
||||
} else {
|
||||
signedTx = await _signTransactionInternal(
|
||||
message: message,
|
||||
signers: signers,
|
||||
commitment: commitment,
|
||||
recentBlockhash: recentBlockhash,
|
||||
);
|
||||
}
|
||||
|
||||
sendTx() async => await sendTransaction(
|
||||
signedTransaction: signedTx,
|
||||
commitment: commitment,
|
||||
|
@ -360,7 +423,6 @@ class SolanaWalletClient {
|
|||
required double inputAmount,
|
||||
required String destinationAddress,
|
||||
required Ed25519HDKeyPair ownerKeypair,
|
||||
required RecentBlockhash recentBlockhash,
|
||||
required Commitment commitment,
|
||||
}) async {
|
||||
final destinationOwner = Ed25519HDPublicKey.fromBase58(destinationAddress);
|
||||
|
@ -408,8 +470,18 @@ class SolanaWalletClient {
|
|||
);
|
||||
|
||||
final message = Message(instructions: [instruction]);
|
||||
|
||||
final signers = [ownerKeypair];
|
||||
|
||||
RecentBlockhash recentBlockhash = await _getRecentBlockhash(commitment);
|
||||
|
||||
final fee = await _getFeeFromCompiledMessage(
|
||||
message,
|
||||
signers.first.publicKey,
|
||||
recentBlockhash,
|
||||
commitment,
|
||||
);
|
||||
|
||||
final signedTx = await _signTransactionInternal(
|
||||
message: message,
|
||||
signers: signers,
|
||||
|
@ -417,12 +489,6 @@ class SolanaWalletClient {
|
|||
recentBlockhash: recentBlockhash,
|
||||
);
|
||||
|
||||
final fee = await _getFeeFromCompiledMessage(
|
||||
message,
|
||||
recentBlockhash,
|
||||
signers.first.publicKey,
|
||||
);
|
||||
|
||||
sendTx() async => await sendTransaction(
|
||||
signedTransaction: signedTx,
|
||||
commitment: commitment,
|
||||
|
@ -438,19 +504,6 @@ class SolanaWalletClient {
|
|||
return pendingTransaction;
|
||||
}
|
||||
|
||||
Future<double> _getFeeFromCompiledMessage(
|
||||
Message message, RecentBlockhash recentBlockhash, Ed25519HDPublicKey feePayer) async {
|
||||
final compile = message.compile(
|
||||
recentBlockhash: recentBlockhash.blockhash,
|
||||
feePayer: feePayer,
|
||||
);
|
||||
|
||||
final base64Message = base64Encode(compile.toByteArray().toList());
|
||||
|
||||
final fee = await getGasForMessage(base64Message);
|
||||
return fee;
|
||||
}
|
||||
|
||||
Future<SignedTx> _signTransactionInternal({
|
||||
required Message message,
|
||||
required List<Ed25519HDKeyPair> signers,
|
||||
|
@ -466,13 +519,18 @@ class SolanaWalletClient {
|
|||
required SignedTx signedTransaction,
|
||||
required Commitment commitment,
|
||||
}) async {
|
||||
final signature = await _client!.rpcClient.sendTransaction(
|
||||
signedTransaction.encode(),
|
||||
preflightCommitment: commitment,
|
||||
);
|
||||
try {
|
||||
final signature = await _client!.rpcClient.sendTransaction(
|
||||
signedTransaction.encode(),
|
||||
preflightCommitment: commitment,
|
||||
);
|
||||
|
||||
_client!.waitForSignatureStatus(signature, status: commitment);
|
||||
_client!.waitForSignatureStatus(signature, status: commitment);
|
||||
|
||||
return signature;
|
||||
return signature;
|
||||
} catch (e) {
|
||||
print('Error while sending transaction: ${e.toString()}');
|
||||
throw Exception(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,9 @@ abstract class SolanaWalletBase
|
|||
|
||||
late SolanaWalletClient _client;
|
||||
|
||||
@observable
|
||||
double? estimatedFee;
|
||||
|
||||
Timer? _transactionsUpdateTimer;
|
||||
|
||||
late final Box<SPLToken> splTokensBox;
|
||||
|
@ -171,6 +174,14 @@ abstract class SolanaWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _getEstimatedFees() async {
|
||||
try {
|
||||
estimatedFee = await _client.getEstimatedFee(_walletKeyPair!);
|
||||
} catch (e) {
|
||||
estimatedFee = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
final solCredentials = credentials as SolanaTransactionCredentials;
|
||||
|
@ -188,6 +199,8 @@ abstract class SolanaWalletBase
|
|||
|
||||
double totalAmount = 0.0;
|
||||
|
||||
bool isSendAll = false;
|
||||
|
||||
if (hasMultiDestination) {
|
||||
if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) {
|
||||
throw SolanaTransactionWrongBalanceException(transactionCurrency);
|
||||
|
@ -204,9 +217,15 @@ abstract class SolanaWalletBase
|
|||
} else {
|
||||
final output = outputs.first;
|
||||
|
||||
final totalOriginalAmount = double.parse(output.cryptoAmount ?? '0.0');
|
||||
isSendAll = output.sendAll;
|
||||
|
||||
totalAmount = output.sendAll ? walletBalanceForCurrency : totalOriginalAmount;
|
||||
if (isSendAll) {
|
||||
totalAmount = walletBalanceForCurrency;
|
||||
} else {
|
||||
final totalOriginalAmount = double.parse(output.cryptoAmount ?? '0.0');
|
||||
|
||||
totalAmount = totalOriginalAmount;
|
||||
}
|
||||
|
||||
if (walletBalanceForCurrency < totalAmount) {
|
||||
throw SolanaTransactionWrongBalanceException(transactionCurrency);
|
||||
|
@ -228,6 +247,7 @@ abstract class SolanaWalletBase
|
|||
destinationAddress: solCredentials.outputs.first.isParsedAddress
|
||||
? solCredentials.outputs.first.extractedAddress!
|
||||
: solCredentials.outputs.first.address,
|
||||
isSendAll: isSendAll,
|
||||
);
|
||||
|
||||
return pendingSolanaTransaction;
|
||||
|
@ -269,7 +289,10 @@ abstract class SolanaWalletBase
|
|||
Future<void> _updateSPLTokenTransactions() async {
|
||||
List<SolanaTransactionModel> splTokenTransactions = [];
|
||||
|
||||
for (var token in balance.keys) {
|
||||
// Make a copy of keys to avoid concurrent modification
|
||||
var tokenKeys = List<CryptoCurrency>.from(balance.keys);
|
||||
|
||||
for (var token in tokenKeys) {
|
||||
if (token is SPLToken) {
|
||||
final tokenTxs = await _client.getSPLTokenTransfers(
|
||||
token.mintAddress,
|
||||
|
@ -326,6 +349,7 @@ abstract class SolanaWalletBase
|
|||
_updateBalance(),
|
||||
_updateNativeSOLTransactions(),
|
||||
_updateSPLTokenTransactions(),
|
||||
_getEstimatedFees(),
|
||||
]);
|
||||
|
||||
syncStatus = SyncedSyncStatus();
|
||||
|
@ -433,18 +457,22 @@ abstract class SolanaWalletBase
|
|||
final mintPublicKey = Ed25519HDPublicKey.fromBase58(mintAddress);
|
||||
|
||||
// Fetch token's metadata account
|
||||
final token = await solanaClient!.rpcClient.getMetadata(mint: mintPublicKey);
|
||||
try {
|
||||
final token = await solanaClient!.rpcClient.getMetadata(mint: mintPublicKey);
|
||||
|
||||
if (token == null) {
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return SPLToken.fromMetadata(
|
||||
name: token.name,
|
||||
mint: token.mint,
|
||||
symbol: token.symbol,
|
||||
mintAddress: mintAddress,
|
||||
);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return SPLToken.fromMetadata(
|
||||
name: token.name,
|
||||
mint: token.mint,
|
||||
symbol: token.symbol,
|
||||
mintAddress: mintAddress,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -475,9 +503,9 @@ abstract class SolanaWalletBase
|
|||
}
|
||||
|
||||
_transactionsUpdateTimer = Timer.periodic(const Duration(seconds: 20), (_) {
|
||||
_updateSPLTokenTransactions();
|
||||
_updateNativeSOLTransactions();
|
||||
_updateBalance();
|
||||
_updateNativeSOLTransactions();
|
||||
_updateSPLTokenTransactions();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
|
|||
|
||||
await wallet.init();
|
||||
wallet.addInitialTokens();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
|
@ -46,16 +47,31 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
|
|||
Future<SolanaWallet> openWallet(String name, String password) async {
|
||||
final walletInfo =
|
||||
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||
final wallet = await SolanaWalletBase.open(
|
||||
name: name,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
await wallet.save();
|
||||
try {
|
||||
final wallet = await SolanaWalletBase.open(
|
||||
name: name,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
|
||||
return wallet;
|
||||
await wallet.init();
|
||||
await wallet.save();
|
||||
saveBackup(name);
|
||||
return wallet;
|
||||
} catch (_) {
|
||||
await restoreWalletFilesFromBackup(name);
|
||||
|
||||
final wallet = await SolanaWalletBase.open(
|
||||
name: name,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
|
||||
await wallet.init();
|
||||
await wallet.save();
|
||||
return wallet;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -110,6 +126,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
|
|||
password: password, name: currentName, walletInfo: currentWalletInfo);
|
||||
|
||||
await currentWallet.renameWalletFiles(newName);
|
||||
await saveBackup(newName);
|
||||
|
||||
final newWalletInfo = currentWalletInfo;
|
||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||
|
|
|
@ -74,8 +74,22 @@ class CWSolana extends Solana {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> addSPLToken(WalletBase wallet, CryptoCurrency token) async =>
|
||||
await (wallet as SolanaWallet).addSPLToken(token as SPLToken);
|
||||
Future<void> addSPLToken(
|
||||
WalletBase wallet,
|
||||
CryptoCurrency token,
|
||||
String contractAddress,
|
||||
) async {
|
||||
final splToken = SPLToken(
|
||||
name: token.name,
|
||||
symbol: token.title,
|
||||
mintAddress: contractAddress,
|
||||
decimal: token.decimals,
|
||||
mint: token.name.toUpperCase(),
|
||||
enabled: token.enabled,
|
||||
);
|
||||
|
||||
await (wallet as SolanaWallet).addSPLToken(splToken);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteSPLToken(WalletBase wallet, CryptoCurrency token) async =>
|
||||
|
@ -115,4 +129,9 @@ class CWSolana extends Solana {
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
double? getEstimateFees(WalletBase wallet) {
|
||||
return (wallet as SolanaWallet).estimatedFee;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -195,12 +195,14 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
|||
onPressed: () async {
|
||||
if (_formKey.currentState!.validate() &&
|
||||
(!_showDisclaimer || _disclaimerChecked)) {
|
||||
await widget.homeSettingsViewModel.addToken(Erc20Token(
|
||||
name: _tokenNameController.text,
|
||||
symbol: _tokenSymbolController.text,
|
||||
await widget.homeSettingsViewModel.addToken(
|
||||
token: CryptoCurrency(
|
||||
name: _tokenNameController.text,
|
||||
title: _tokenSymbolController.text.toUpperCase(),
|
||||
decimals: int.parse(_tokenDecimalController.text),
|
||||
),
|
||||
contractAddress: _contractAddressController.text,
|
||||
decimal: int.parse(_tokenDecimalController.text),
|
||||
));
|
||||
);
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
|
|
@ -323,8 +323,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
|||
? sendViewModel.allAmountValidator
|
||||
: sendViewModel.amountValidator,
|
||||
),
|
||||
if (!sendViewModel.isBatchSending &&
|
||||
sendViewModel.shouldDisplaySendALL)
|
||||
if (!sendViewModel.isBatchSending)
|
||||
Positioned(
|
||||
top: 2,
|
||||
right: 0,
|
||||
|
@ -456,7 +455,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
|||
if (sendViewModel.hasFees)
|
||||
Observer(
|
||||
builder: (_) => GestureDetector(
|
||||
onTap: () => _setTransactionPriority(context),
|
||||
onTap: sendViewModel.hasFeesPriority
|
||||
? () => _setTransactionPriority(context)
|
||||
: () {},
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: Row(
|
||||
|
|
|
@ -44,17 +44,37 @@ abstract class HomeSettingsViewModelBase with Store {
|
|||
@action
|
||||
void setPinNativeToken(bool value) => _settingsStore.pinNativeTokenAtTop = value;
|
||||
|
||||
Future<void> addToken(CryptoCurrency token) async {
|
||||
Future<void> addToken({
|
||||
required String contractAddress,
|
||||
required CryptoCurrency token,
|
||||
}) async {
|
||||
if (_balanceViewModel.wallet.type == WalletType.ethereum) {
|
||||
await ethereum!.addErc20Token(_balanceViewModel.wallet, token);
|
||||
final erc20token = Erc20Token(
|
||||
name: token.name,
|
||||
symbol: token.title,
|
||||
decimal: token.decimals,
|
||||
contractAddress: contractAddress,
|
||||
);
|
||||
|
||||
await ethereum!.addErc20Token(_balanceViewModel.wallet, erc20token);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.polygon) {
|
||||
await polygon!.addErc20Token(_balanceViewModel.wallet, token);
|
||||
final polygonToken = Erc20Token(
|
||||
name: token.name,
|
||||
symbol: token.title,
|
||||
decimal: token.decimals,
|
||||
contractAddress: contractAddress,
|
||||
);
|
||||
await polygon!.addErc20Token(_balanceViewModel.wallet, polygonToken);
|
||||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
await solana!.addSPLToken(_balanceViewModel.wallet, token);
|
||||
await solana!.addSPLToken(
|
||||
_balanceViewModel.wallet,
|
||||
token,
|
||||
contractAddress,
|
||||
);
|
||||
}
|
||||
|
||||
_updateTokensList();
|
||||
|
@ -117,7 +137,8 @@ abstract class HomeSettingsViewModelBase with Store {
|
|||
}
|
||||
|
||||
if (_balanceViewModel.wallet.type == WalletType.solana) {
|
||||
solana!.addSPLToken(_balanceViewModel.wallet, token);
|
||||
final address = solana!.getTokenAddress(token);
|
||||
solana!.addSPLToken(_balanceViewModel.wallet, token, address);
|
||||
}
|
||||
|
||||
_refreshTokensList();
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart';
|
|||
import 'package:cake_wallet/haven/haven.dart';
|
||||
import 'package:cake_wallet/polygon/polygon.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:cake_wallet/solana/solana.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -116,6 +117,10 @@ abstract class OutputBase with Store {
|
|||
@computed
|
||||
double get estimatedFee {
|
||||
try {
|
||||
if (_wallet.type == WalletType.solana) {
|
||||
return solana!.getEstimateFees(_wallet) ?? 0.0;
|
||||
}
|
||||
|
||||
final fee = _wallet.calculateEstimatedFee(
|
||||
_settingsStore.priority[_wallet.type]!, formattedCryptoAmount);
|
||||
|
||||
|
|
|
@ -106,8 +106,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
|||
@computed
|
||||
bool get isBatchSending => outputs.length > 1;
|
||||
|
||||
bool get shouldDisplaySendALL => walletType != WalletType.solana;
|
||||
|
||||
@computed
|
||||
String get pendingTransactionFiatAmount {
|
||||
if (pendingTransaction == null) {
|
||||
|
@ -208,6 +206,11 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
|||
@computed
|
||||
bool get hasFees => wallet.type != WalletType.nano && wallet.type != WalletType.banano;
|
||||
|
||||
@computed
|
||||
bool get hasFeesPriority =>
|
||||
wallet.type != WalletType.nano &&
|
||||
wallet.type != WalletType.banano &&
|
||||
wallet.type != WalletType.solana;
|
||||
@observable
|
||||
CryptoCurrency selectedCryptoCurrency;
|
||||
|
||||
|
|
|
@ -948,7 +948,11 @@ abstract class Solana {
|
|||
required CryptoCurrency currency,
|
||||
});
|
||||
List<CryptoCurrency> getSPLTokenCurrencies(WalletBase wallet);
|
||||
Future<void> addSPLToken(WalletBase wallet, CryptoCurrency token);
|
||||
Future<void> addSPLToken(
|
||||
WalletBase wallet,
|
||||
CryptoCurrency token,
|
||||
String contractAddress,
|
||||
);
|
||||
Future<void> deleteSPLToken(WalletBase wallet, CryptoCurrency token);
|
||||
Future<CryptoCurrency?> getSPLToken(WalletBase wallet, String contractAddress);
|
||||
|
||||
|
@ -956,6 +960,7 @@ abstract class Solana {
|
|||
double getTransactionAmountRaw(TransactionInfo transactionInfo);
|
||||
String getTokenAddress(CryptoCurrency asset);
|
||||
List<int>? getValidationLength(CryptoCurrency type);
|
||||
double? getEstimateFees(WalletBase wallet);
|
||||
}
|
||||
|
||||
""";
|
||||
|
|
Loading…
Reference in a new issue