Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CW-829 Solana Enhancements #1858

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 48 additions & 5 deletions cw_solana/lib/solana_client.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'dart:math' as math;

import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_solana/pending_solana_transaction.dart';
import 'package:cw_solana/solana_balance.dart';
import 'package:cw_solana/solana_exceptions.dart';
import 'package:cw_solana/solana_transaction_model.dart';
import 'package:http/http.dart' as http;
import 'package:solana/dto.dart';
Expand Down Expand Up @@ -180,7 +181,7 @@ class SolanaWalletClient {
bool isOutgoingTx = transfer.source == publicKey.toBase58();

double amount = (double.tryParse(transfer.amount) ?? 0.0) /
pow(10, splTokenDecimal ?? 9);
math.pow(10, splTokenDecimal ?? 9);

transactions.add(
SolanaTransactionModel(
Expand Down Expand Up @@ -276,6 +277,7 @@ class SolanaWalletClient {
required String destinationAddress,
required Ed25519HDKeyPair ownerKeypair,
required bool isSendAll,
required double solBalance,
String? tokenMint,
List<String> references = const [],
}) async {
Expand All @@ -290,6 +292,7 @@ class SolanaWalletClient {
ownerKeypair: ownerKeypair,
commitment: commitment,
isSendAll: isSendAll,
solBalance: solBalance,
);
return pendingNativeTokenTransaction;
} else {
Expand All @@ -301,6 +304,7 @@ class SolanaWalletClient {
destinationAddress: destinationAddress,
ownerKeypair: ownerKeypair,
commitment: commitment,
solBalance: solBalance,
);
return pendingSPLTokenTransaction;
}
Expand Down Expand Up @@ -353,6 +357,23 @@ class SolanaWalletClient {
return fee;
}

Future<bool> hasSufficientFundsLeftForRent({
required double inputAmount,
required double solBalance,
required double fee,
}) async {
final rent =
await _client!.getMinimumBalanceForMintRentExemption(commitment: Commitment.confirmed);

final rentInSol = (rent / lamportsPerSol).toDouble();

final remnant = solBalance - (inputAmount + fee);

if (remnant > rentInSol) return true;

return false;
}

Future<PendingSolanaTransaction> _signNativeTokenTransaction({
required String tokenTitle,
required int tokenDecimals,
Expand All @@ -361,6 +382,7 @@ class SolanaWalletClient {
required Ed25519HDKeyPair ownerKeypair,
required Commitment commitment,
required bool isSendAll,
required double solBalance,
}) async {
// Convert SOL to lamport
int lamports = (inputAmount * lamportsPerSol).toInt();
Expand All @@ -378,6 +400,16 @@ class SolanaWalletClient {
commitment,
);

bool hasSufficientFundsLeft = await hasSufficientFundsLeftForRent(
inputAmount: inputAmount,
fee: fee,
solBalance: solBalance,
);

if (!hasSufficientFundsLeft) {
throw SolanaSignNativeTokenTransactionRentException();
}

SignedTx signedTx;
if (isSendAll) {
final feeInLamports = (fee * lamportsPerSol).toInt();
Expand Down Expand Up @@ -425,6 +457,7 @@ class SolanaWalletClient {
required String destinationAddress,
required Ed25519HDKeyPair ownerKeypair,
required Commitment commitment,
required double solBalance,
}) async {
final destinationOwner = Ed25519HDPublicKey.fromBase58(destinationAddress);
final mint = Ed25519HDPublicKey.fromBase58(tokenMint);
Expand All @@ -447,7 +480,7 @@ class SolanaWalletClient {
// Throw an appropriate exception if the sender has no associated
// token account
if (associatedSenderAccount == null) {
throw NoAssociatedTokenAccountException(ownerKeypair.address, mint.toBase58());
throw SolanaNoAssociatedTokenAccountException(ownerKeypair.address, mint.toBase58());
}

try {
Expand All @@ -457,11 +490,11 @@ class SolanaWalletClient {
funder: ownerKeypair,
);
} catch (e) {
throw Exception('Insufficient SOL balance to complete this transaction: ${e.toString()}');
throw SolanaCreateAssociatedTokenAccountException(e.toString());
}

// Input by the user
final amount = (inputAmount * pow(10, tokenDecimals)).toInt();
final amount = (inputAmount * math.pow(10, tokenDecimals)).toInt();

final instruction = TokenInstruction.transfer(
source: Ed25519HDPublicKey.fromBase58(associatedSenderAccount.pubkey),
Expand All @@ -483,6 +516,16 @@ class SolanaWalletClient {
commitment,
);

bool hasSufficientFundsLeft = await hasSufficientFundsLeftForRent(
inputAmount: inputAmount,
fee: fee,
solBalance: solBalance,
);

if (!hasSufficientFundsLeft) {
throw SolanaSignSPLTokenTransactionRentException();
}

final signedTx = await _signTransactionInternal(
message: message,
signers: signers,
Expand Down
17 changes: 17 additions & 0 deletions cw_solana/lib/solana_exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,20 @@ class SolanaTransactionWrongBalanceException implements Exception {
@override
String toString() => exceptionMessage;
}

class SolanaSignNativeTokenTransactionRentException implements Exception {}

class SolanaCreateAssociatedTokenAccountException implements Exception {
final String exceptionMessage;

SolanaCreateAssociatedTokenAccountException(this.exceptionMessage);
}

class SolanaSignSPLTokenTransactionRentException implements Exception {}

class SolanaNoAssociatedTokenAccountException implements Exception {
const SolanaNoAssociatedTokenAccountException(this.account, this.mint);

final String account;
final String mint;
}
3 changes: 3 additions & 0 deletions cw_solana/lib/solana_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ abstract class SolanaWalletBase

final walletBalanceForCurrency = balance[transactionCurrency]!.balance;

final solBalance = balance[CryptoCurrency.sol]!.balance;

double totalAmount = 0.0;

bool isSendAll = false;
Expand Down Expand Up @@ -279,6 +281,7 @@ abstract class SolanaWalletBase
? solCredentials.outputs.first.extractedAddress!
: solCredentials.outputs.first.address,
isSendAll: isSendAll,
solBalance: solBalance,
);

return pendingSolanaTransaction;
Expand Down
41 changes: 26 additions & 15 deletions lib/view_model/send/send_view_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/entities/transaction_description.dart';
Expand All @@ -13,7 +12,6 @@ import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/src/screens/ur/animated_ur_page.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
Expand All @@ -28,6 +26,7 @@ import 'package:cw_core/unspent_coin_type.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_solana/solana_exceptions.dart';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
Expand Down Expand Up @@ -139,7 +138,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
if (walletType == WalletType.ethereum && selectedCryptoCurrency == CryptoCurrency.eth)
return false;

if (walletType == WalletType.polygon && selectedCryptoCurrency == CryptoCurrency.matic)
if (walletType == WalletType.polygon && selectedCryptoCurrency == CryptoCurrency.matic)
return false;

return true;
Expand Down Expand Up @@ -426,7 +425,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
//
// state = FailureState(errorMsg);
// } else {
state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency));
state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency));
// }
}
return null;
Expand Down Expand Up @@ -486,10 +485,11 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor

try {
state = TransactionCommitting();

if (pendingTransaction!.shouldCommitUR()) {
final urstr = await pendingTransaction!.commitUR();
final result = await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage, arguments: urstr);
final result =
await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage, arguments: urstr);
if (result == null) {
state = FailureState("Canceled by user");
return;
Expand All @@ -502,18 +502,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
nano!.updateTransactions(wallet);
}


if (pendingTransaction!.id.isNotEmpty) {

final descriptionKey = '${pendingTransaction!.id}_${wallet.walletAddresses.primaryAddress}';
_settingsStore.shouldSaveRecipientAddress
? await transactionDescriptionBox.add(TransactionDescription(
id: descriptionKey,
recipientAddress: address,
transactionNote: note))
: await transactionDescriptionBox.add(TransactionDescription(
id: descriptionKey,
transactionNote: note));
id: descriptionKey, recipientAddress: address, transactionNote: note))
: await transactionDescriptionBox
.add(TransactionDescription(id: descriptionKey, transactionNote: note));
}

state = TransactionCommitted();
Expand Down Expand Up @@ -675,10 +670,26 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
lamportsNeeded != null ? ((lamportsNeeded + 5000) / lamportsPerSol) : 0.0;
return S.current.insufficient_lamports(solValueNeeded.toString());
} else {
printV("No match found.");
return S.current.insufficient_lamport_for_tx;
}
}

if (error is SolanaSignNativeTokenTransactionRentException) {
return S.current.solana_sign_native_transaction_rent_exception;
}

if (error is SolanaCreateAssociatedTokenAccountException) {
return S.current.solana_create_associated_token_account_exception;
}

if (error is SolanaSignSPLTokenTransactionRentException) {
return S.current.solana_sign_spl_token_transaction_rent_exception;
}

if (error is SolanaNoAssociatedTokenAccountException) {
return S.current.solana_no_associated_token_account_exception;
}

if (errorMessage.contains('insufficient funds for rent')) {
return S.current.insufficientFundsForRentError;
}
Expand Down
4 changes: 4 additions & 0 deletions res/values/strings_ar.arb
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,10 @@
"silent_payments_settings": "إعدادات المدفوعات الصامتة",
"single_seed_wallets_group": "محافظ بذرة واحدة",
"slidable": "قابل للانزلاق",
"solana_create_associated_token_account_exception": "خطأ في إنشاء حساب رمز المرتبط بعنوان المستلم.",
"solana_no_associated_token_account_exception": "لا يوجد حساب مميز مرتبط بهذا العنوان.",
"solana_sign_native_transaction_rent_exception": "لا يمكن إكمال المعاملة. غادر SOL غير كاف للإيجار بعد المعاملة. يرجى أن تصل إلى رصيد SOL أو تقليل كمية SOL التي ترسلها.",
"solana_sign_spl_token_transaction_rent_exception": "لا يمكن إكمال المعاملة. غادر SOL غير كاف للإيجار بعد المعاملة. يرجى أن تصل إلى توازن سولك.",
"sort_by": "ترتيب حسب",
"spend_key_private": "مفتاح الإنفاق (خاص)",
"spend_key_public": "مفتاح الإنفاق (عام)",
Expand Down
4 changes: 4 additions & 0 deletions res/values/strings_bg.arb
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,10 @@
"silent_payments_settings": "Настройки за безшумни плащания",
"single_seed_wallets_group": "Портфейли с единични семена",
"slidable": "Плъзгащ се",
"solana_create_associated_token_account_exception": "Грешка Създаване на свързана сметка за жетони за адреса на получател.",
"solana_no_associated_token_account_exception": "Няма свързана сметка за този адрес.",
"solana_sign_native_transaction_rent_exception": "Транзакцията не може да бъде завършена. Недостатъчен сол оставен под наем след транзакция. Любезно допълнете баланса си на SOL или намалете количеството SOL, което изпращате.",
"solana_sign_spl_token_transaction_rent_exception": "Транзакцията не може да бъде завършена. Недостатъчен сол оставен под наем след транзакция. Любезно допълнете баланса си на SOL.",
"sort_by": "Сортирай по",
"spend_key_private": "Spend key (таен)",
"spend_key_public": "Spend key (публичен)",
Expand Down
4 changes: 4 additions & 0 deletions res/values/strings_cs.arb
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,10 @@
"silent_payments_settings": "Nastavení tichých plateb",
"single_seed_wallets_group": "Jednorázové peněženky",
"slidable": "Posuvné",
"solana_create_associated_token_account_exception": "Vytvoření chyby přidruženého účtu tokenů pro adresu příjmu.",
"solana_no_associated_token_account_exception": "Pro tuto adresu není přidružen žádný přidružený token.",
"solana_sign_native_transaction_rent_exception": "Transakce nelze dokončit. Po transakci nedostatek Sol odešel k pronájmu. Laskavě doplňte rovnováhu SOL nebo snižte množství SOL, které odesíláte.",
"solana_sign_spl_token_transaction_rent_exception": "Transakce nelze dokončit. Po transakci nedostatek Sol odešel k pronájmu. Laskavě doplňte rovnováhu SOL.",
"sort_by": "Seřazeno podle",
"spend_key_private": "Klíč pro platby (soukromý)",
"spend_key_public": "Klíč pro platby (veřejný)",
Expand Down
6 changes: 5 additions & 1 deletion res/values/strings_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -498,8 +498,8 @@
"placeholder_transactions": "Ihre Transaktionen werden hier angezeigt",
"please_fill_totp": "Bitte geben Sie den 8-stelligen Code ein, der auf Ihrem anderen Gerät vorhanden ist",
"please_make_selection": "Bitte treffen Sie unten eine Auswahl zum Erstellen oder Wiederherstellen Ihrer Wallet.",
"Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.",
"please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.",
"Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.",
"please_select": "Bitte auswählen:",
"please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.",
"please_try_to_connect_to_another_node": "Bitte versuchen Sie, sich mit einem anderen Knoten zu verbinden",
Expand Down Expand Up @@ -738,6 +738,10 @@
"silent_payments_settings": "Einstellungen für stille Zahlungen",
"single_seed_wallets_group": "Einzelne Wallets",
"slidable": "Verschiebbar",
"solana_create_associated_token_account_exception": "Fehler beim Erstellen des zugehörigen Token -Kontos für die Empfängeradresse.",
"solana_no_associated_token_account_exception": "Für diese Adresse ist kein Token -Konto zugeordnet.",
"solana_sign_native_transaction_rent_exception": "Transaktion kann nicht abgeschlossen werden. Unzureichende Sol ließen nach der Transaktion zur Miete gelassen. Bitte geben Sie Ihre SOL -Balance auf oder reduzieren Sie die Menge an SOL, die Sie senden.",
"solana_sign_spl_token_transaction_rent_exception": "Transaktion kann nicht abgeschlossen werden. Unzureichende Sol ließen nach der Transaktion zur Miete gelassen. Bitte geben Sie Ihre SOL -Balance auf.",
"sort_by": "Sortiere nach",
"spend_key_private": "Spend Key (geheim)",
"spend_key_public": "Spend Key (öffentlich)",
Expand Down
4 changes: 4 additions & 0 deletions res/values/strings_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,10 @@
"silent_payments_settings": "Silent Payments settings",
"single_seed_wallets_group": "Single Seed Wallets",
"slidable": "Slidable",
"solana_create_associated_token_account_exception": "Error creating associated token account for receipient address.",
"solana_no_associated_token_account_exception": "There is no associated token account for this address.",
"solana_sign_native_transaction_rent_exception": "Transaction cannot be completed. Insufficient SOL left for rent after transaction. Kindly top up your SOL balance or reduce the amount of SOL you are sending.",
"solana_sign_spl_token_transaction_rent_exception": "Transaction cannot be completed. Insufficient SOL left for rent after transaction. Kindly top up your SOL balance.",
"sort_by": "Sort by",
"spend_key_private": "Spend key (private)",
"spend_key_public": "Spend key (public)",
Expand Down
4 changes: 4 additions & 0 deletions res/values/strings_es.arb
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,10 @@
"silent_payments_settings": "Configuración de pagos silenciosos",
"single_seed_wallets_group": "Billeteras de semillas individuales",
"slidable": "deslizable",
"solana_create_associated_token_account_exception": "Error a crear una cuenta de token asociada para la dirección recibida.",
"solana_no_associated_token_account_exception": "No hay una cuenta de token asociada para esta dirección.",
"solana_sign_native_transaction_rent_exception": "La transacción no se puede completar. Sol insuficiente que queda en alquiler después de la transacción. Por favor, supere su saldo de sol o reduzca la cantidad de sol que está enviando.",
"solana_sign_spl_token_transaction_rent_exception": "La transacción no se puede completar. Sol insuficiente que queda en alquiler después de la transacción. Por favor, supere su balance de sol.",
"sort_by": "Ordenar por",
"spend_key_private": "Llave de gasto (privada)",
"spend_key_public": "Llave de gasto (pública)",
Expand Down
4 changes: 4 additions & 0 deletions res/values/strings_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,10 @@
"silent_payments_settings": "Paramètres de paiement silencieux",
"single_seed_wallets_group": "Portefeuilles de semences simples",
"slidable": "Glissable",
"solana_create_associated_token_account_exception": "Création d'erreur Création de jetons associés pour l'adresse détenue.",
"solana_no_associated_token_account_exception": "Il n'y a pas de compte de jeton associé pour cette adresse.",
"solana_sign_native_transaction_rent_exception": "La transaction ne peut pas être terminée. Sol insuffisant laissé à la location après la transaction. Veuillez compléter votre solde SOL ou réduire la quantité de Sol que vous envoyez.",
"solana_sign_spl_token_transaction_rent_exception": "La transaction ne peut pas être terminée. Sol insuffisant laissé à la location après la transaction. Veuillez compléter votre solde de Sol.",
"sort_by": "Trier par",
"spend_key_private": "Clef de dépense (spend key) (privée)",
"spend_key_public": "Clef de dépense (spend key) (publique)",
Expand Down
4 changes: 4 additions & 0 deletions res/values/strings_ha.arb
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,10 @@
"silent_payments_settings": "Saitunan Silent",
"single_seed_wallets_group": "Guaro",
"slidable": "Mai iya zamewa",
"solana_create_associated_token_account_exception": "Kuskuren ƙirƙirar asusun Asusun da aka danganta don Adireshin karɓar karɓa.",
"solana_no_associated_token_account_exception": "Babu wani haɗin yanar gizo mai alaƙa don wannan adireshin.",
"solana_sign_native_transaction_rent_exception": "Ma'amala ba za a iya kammala ba. Rashin cancanta na Sol ya bar haya bayan ma'amala. Da kyau a saman ma'auni na Solku ko rage adadin Sol da kuke aikawa.",
"solana_sign_spl_token_transaction_rent_exception": "Ma'amala ba za a iya kammala ba. Rashin cancanta na Sol ya bar haya bayan ma'amala. Da kyau sama da ma'auni na Sol.",
"sort_by": "Kasa",
"spend_key_private": "makullin biya (maɓallin kalmar sirri)",
"spend_key_public": "makullin biya (maɓallin jama'a)",
Expand Down
Loading