Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
716813c
feat(spl): add mock SolanaTokenWallet impl
sneurlax Oct 31, 2025
70b7f51
feat(spl): add Solana token (SPL) state mgmt providers
sneurlax Oct 31, 2025
9897a98
feat(spl): Solana token (SPL) UI components
sneurlax Oct 31, 2025
575c715
feat(spl): register Solana token (SPL) view routes in nav
sneurlax Oct 31, 2025
b991457
feat(spl): flag/enable token support for Solana
sneurlax Oct 31, 2025
e8327b6
feat(spl): add Solana token (SPL) model and contract abstraction
sneurlax Nov 1, 2025
ba5492e
feat(spl): add Solana token storage and state mgmt providers
sneurlax Nov 1, 2025
4fe52e0
feat(spl): implement Solana token selection
sneurlax Nov 1, 2025
4ec577c
fix(spl): correct USDC mint address for Solana
sneurlax Nov 1, 2025
651f1fc
feat(spl): working token ownership check
sneurlax Nov 2, 2025
de1b0ce
feat(spl): implement balance extraction in SolanaTokenAPI
sneurlax Nov 2, 2025
230ed7e
feat(spl): add SplToken to AmountFormatter for ticker display
sneurlax Nov 2, 2025
03617f6
feat(spl): db methods and schema for Solana token (SPL) tokens
sneurlax Nov 3, 2025
8d42100
feat(spl): add token selection and list widgets for Solana tokens (SPL)
sneurlax Nov 3, 2025
08346f1
feat(spl): add mobile Solana token detail view
sneurlax Nov 3, 2025
a25e51a
feat(spl): add desktop Solana token (SPL) detail view
sneurlax Nov 3, 2025
9f98c41
feat(spl): add Solana wallet detection to MyTokensView
sneurlax Nov 4, 2025
71178e6
feat(spl): add Solana token selection in wallet token editor
sneurlax Nov 4, 2025
d1f79a3
feat(spl): register routes for Solana token (SPL) views
sneurlax Nov 4, 2025
c72c9c1
fix(spl): handle missing Ethereum token wallet in shared components
sneurlax Nov 5, 2025
66da8bb
feat(spl): add Solana token handling in wallet send/receive view
sneurlax Nov 5, 2025
fcd68be
fix(spl): replace Ethereum-only transaction list with placeholder
sneurlax Nov 5, 2025
c47a93f
fix(spl): Solana token specific views for mobile (WIP)
sneurlax Nov 6, 2025
bf7dacb
fix(spl): Solana token specific transaction list widget
sneurlax Nov 6, 2025
c2ee6ec
ui(spl): SPL token icon
sneurlax Nov 6, 2025
17edf78
fix(spl): sol icon fix
sneurlax Nov 6, 2025
a227e06
feat(spl): implement balance fetching and fix ticker/symbol use in ui
sneurlax Nov 6, 2025
4c5a364
feat(spl): Solana token (SPL) sending scaffolding
sneurlax Nov 11, 2025
cda032b
fix(spl): race condition fix
sneurlax Nov 11, 2025
fdcde9d
feat(spl): desktop sol token send
sneurlax Nov 11, 2025
8ea00df
fix(spl): amount formatting
sneurlax Nov 11, 2025
81ebbf6
fix(spl): pass parent sol wallet of child token wallet
sneurlax Nov 11, 2025
cd97339
feat(spl): sol token fee estimation
sneurlax Nov 11, 2025
146a157
feat(spl): fetch sol token balance on refresh
sneurlax Nov 11, 2025
5b97739
fix(spl): set initial sync status according to parent wallet
sneurlax Nov 11, 2025
a818a5f
feat(spl): sol token price fetching
sneurlax Nov 11, 2025
ea14f97
fix(spl): graceful null check operator handling
sneurlax Nov 11, 2025
87b3e5c
fix(spl): prepare to replace novel token balance provider to be like eth
sneurlax Nov 12, 2025
a8906cf
fix(spl): replace novel token balance provider to follow eth's example
sneurlax Nov 12, 2025
cc4ecbb
refactor(spl): don't create separate Amount objs
sneurlax Nov 12, 2025
4a860f0
refactor(spl): remove unneeded sol wallet token address provider
sneurlax Nov 12, 2025
aa87ab1
feat(spl): cache token transfers
sneurlax Nov 14, 2025
c3a1cdc
feat(spl): cache local sol & spl txs and update txs as they confirm
sneurlax Nov 14, 2025
313fada
feat(spl): add custom token mint addresses storage to WalletInfo
sneurlax Nov 19, 2025
c5380d9
feat(spl): Token-2022 support for SPL token balance fetching
sneurlax Nov 19, 2025
67d4fdd
feat(spl): save custom tokens and include in wallet token list
sneurlax Nov 19, 2025
5e31a29
feat(spl): fetch custom token metadata from db and update bal on open
sneurlax Nov 19, 2025
a93b19b
feat(spl): query database for custom tokens in addition to defaults
sneurlax Nov 19, 2025
0ecc5fd
feat(spl): custom tokens with validation and error handling
sneurlax Nov 19, 2025
0329dc8
feat(spl): add route for AddCustomSolanaTokenView
sneurlax Nov 19, 2025
9a48267
feat(spl): mobile custom token flow
sneurlax Nov 19, 2025
2c874e3
Merge branch 'staging' into feat/spl
sneurlax Nov 19, 2025
efcfd2f
fix(spl): revert changes to stack_theme.dart
sneurlax Nov 19, 2025
9554829
Amount and Balance QoL constructors
julian-CStack Nov 19, 2025
9ba2876
Merge remote-tracking branch 'origin/feat/spl' into feat/spl
sneurlax Nov 19, 2025
5832233
housekeeping (build runner, etc)
julian-CStack Nov 19, 2025
9b8cb12
remove non existent asset getters
julian-CStack Nov 19, 2025
94cac10
clean up token icons
julian-CStack Nov 19, 2025
5d6a0ca
various
julian-CStack Nov 19, 2025
234af02
txv2
julian-CStack Nov 19, 2025
77c6e94
ui(spl): mobile send and receive flows
sneurlax Nov 20, 2025
602e268
ui(spl): mobile token details view
sneurlax Nov 20, 2025
75895a2
ui(spl): desktop token details view
sneurlax Nov 20, 2025
4030002
ui(spl): desktop sol token wallet nav
sneurlax Nov 20, 2025
61e0102
ui(spl): mobile sol token wallet nav
sneurlax Nov 20, 2025
977222d
feat(spl): fee selection
sneurlax Nov 21, 2025
fe1fdf6
fix(spl): mobile fee selection ui fix
sneurlax Nov 21, 2025
6049da3
ui(spl): show tokens after sol wallet creation or restore on mobile
sneurlax Nov 21, 2025
553c2a8
fix(spl): delegate chain refresh to parent wallet
sneurlax Nov 21, 2025
9887fd6
fix(spl): use sol's address validation for tokens, too
sneurlax Nov 21, 2025
5b97929
fix(spl): init rpc even when you skip opening the parent wallet
sneurlax Nov 21, 2025
75427be
feat(spl): implement Token-2022 transfers
sneurlax Nov 21, 2025
a1e4e84
chore(spl): remove prints, log what's important, remove unnecessary docs
sneurlax Nov 21, 2025
658ff80
chore: cleanup
sneurlax Nov 25, 2025
44fed9a
dart format and a couple tweaks/fixes
julian-CStack Nov 25, 2025
6e34517
feat(spl): add sol tokens to add wallet list
sneurlax Nov 25, 2025
71fa54d
refactor(spl): reduce default spl token list
sneurlax Nov 25, 2025
7b0a23e
chore(spl): "SPL" -> "SOL" (we use both SPL and Token-2022 SOL tokens)
sneurlax Nov 25, 2025
659f76f
fix(spl): Set->List to match Eth
sneurlax Nov 26, 2025
14c506c
fix(spl): handle Sol tokens the same way Eth tokens are
sneurlax Nov 26, 2025
70e42e8
refactor(spl): align DefaultSplTokens usage w/ DefaultTokens
sneurlax Nov 26, 2025
8d5cd8f
refactor(spl) "spl"->"sol" where appropriate
sneurlax Nov 26, 2025
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
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ secp256k1.dll
/lib/app_config.g.dart
/android/app/src/main/app_icon-playstore.png

# Dart generated files (Freezed, Riverpod, GoRouter etc..)
lib/**/*.g.dart
lib/**/*.freezed.dart

## other generated project files

pubspec.yaml
Expand Down
24 changes: 24 additions & 0 deletions lib/db/isar/main_db.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class MainDB {
AddressSchema,
AddressLabelSchema,
EthContractSchema,
SolContractSchema,
TransactionBlockExplorerSchema,
StackThemeSchema,
ContactEntrySchema,
Expand All @@ -69,6 +70,7 @@ class MainDB {
WalletInfoMetaSchema,
TokenWalletInfoSchema,
FrostWalletInfoSchema,
WalletSolanaTokenInfoSchema,
],
directory: (await StackFileSystem.applicationIsarDirectory()).path,
// inspector: kDebugMode,
Expand Down Expand Up @@ -621,4 +623,26 @@ class MainDB {
isar.writeTxn(() async {
await isar.ethContracts.putAll(contracts);
});

// ========== Solana =========================================================

// Solana tokens.

QueryBuilder<SolContract, SolContract, QWhere> getSolContracts() =>
isar.solContracts.where();

Future<SolContract?> getSolContract(String tokenMint) =>
isar.solContracts.where().addressEqualTo(tokenMint).findFirst();

SolContract? getSolContractSync(String tokenMint) =>
isar.solContracts.where().addressEqualTo(tokenMint).findFirstSync();

Future<int> putSolContract(SolContract token) => isar.writeTxn(() async {
return await isar.solContracts.put(token);
});

Future<void> putSolContracts(List<SolContract> tokens) =>
isar.writeTxn(() async {
await isar.solContracts.putAll(tokens);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* This file is part of Stack Wallet.
*
* Copyright (c) 2023 Cypher Stack
* All Rights Reserved.
* The code is distributed under GPLv3 license, see LICENSE file for details.
*
*/

import '../../../wallets/crypto_currency/crypto_currency.dart';
import '../../isar/models/solana/sol_contract.dart';
import '../add_wallet_list_entity.dart';

class SolTokenEntity extends AddWalletListEntity {
SolTokenEntity(this.token);

final SolContract token;

@override
CryptoCurrency get cryptoCurrency => Solana(CryptoCurrencyNetwork.main);

@override
String get name => token.name;

@override
String get ticker => token.symbol;

@override
List<Object?> get props =>
[cryptoCurrency.identifier, name, ticker, token.address];
}
31 changes: 15 additions & 16 deletions lib/models/balance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,18 @@ class Balance {
final Amount blockedTotal;
final Amount pendingSpendable;

Balance({
const Balance({
required this.total,
required this.spendable,
required this.blockedTotal,
required this.pendingSpendable,
});

factory Balance.zeroFor({required CryptoCurrency currency}) {
final amount = Amount(
rawValue: BigInt.zero,
fractionDigits: currency.fractionDigits,
);
factory Balance.zeroFor({required CryptoCurrency currency}) =>
.zeroWith(fractionDigits: currency.fractionDigits);

factory Balance.zeroWith({required int fractionDigits}) {
final amount = Amount.zeroWith(fractionDigits: fractionDigits);
return Balance(
total: amount,
spendable: amount,
Expand All @@ -41,11 +40,11 @@ class Balance {
}

String toJsonIgnoreCoin() => jsonEncode({
"total": total.toJsonString(),
"spendable": spendable.toJsonString(),
"blockedTotal": blockedTotal.toJsonString(),
"pendingSpendable": pendingSpendable.toJsonString(),
});
"total": total.toJsonString(),
"spendable": spendable.toJsonString(),
"blockedTotal": blockedTotal.toJsonString(),
"pendingSpendable": pendingSpendable.toJsonString(),
});

// need to fall back to parsing from int due to cached balances being previously
// stored as int values instead of Amounts
Expand Down Expand Up @@ -82,11 +81,11 @@ class Balance {
}

Map<String, dynamic> toMap() => {
"total": total,
"spendable": spendable,
"blockedTotal": blockedTotal,
"pendingSpendable": pendingSpendable,
};
"total": total,
"spendable": spendable,
"blockedTotal": blockedTotal,
"pendingSpendable": pendingSpendable,
};

@override
String toString() {
Expand Down
1 change: 1 addition & 0 deletions lib/models/isar/models/blockchain_data/transaction.dart
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,5 @@ enum TransactionSubType {
sparkSpend, // firo specific
ordinal,
mweb,
splToken, // Solana token.
}
2 changes: 2 additions & 0 deletions lib/models/isar/models/blockchain_data/transaction.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion lib/models/isar/models/contract.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,15 @@
*/

abstract class Contract {
// for possible future use
/// Token/contract address (mint address for Solana, contract address for Ethereum).
String get address;

/// Token name.
String get name;

/// Token symbol.
String get symbol;

/// Token decimals.
int get decimals;
}
28 changes: 14 additions & 14 deletions lib/models/isar/models/ethereum/eth_contract.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

import 'package:isar_community/isar.dart';

import '../contract.dart';

part 'eth_contract.g.dart';
Expand All @@ -26,13 +27,17 @@ class EthContract extends Contract {

Id id = Isar.autoIncrement;

@override
@Index(unique: true, replace: true)
late final String address;

@override
late final String name;

@override
late final String symbol;

@override
late final int decimals;

late final String? abi;
Expand All @@ -50,21 +55,16 @@ class EthContract extends Contract {
List<String>? walletIds,
String? abi,
String? otherData,
}) =>
EthContract(
address: address ?? this.address,
name: name ?? this.name,
symbol: symbol ?? this.symbol,
decimals: decimals ?? this.decimals,
type: type ?? this.type,
abi: abi ?? this.abi,
)..id = id ?? this.id;
}) => EthContract(
address: address ?? this.address,
name: name ?? this.name,
symbol: symbol ?? this.symbol,
decimals: decimals ?? this.decimals,
type: type ?? this.type,
abi: abi ?? this.abi,
)..id = id ?? this.id;
}

// Used in Isar db and stored there as int indexes so adding/removing values
// in this definition should be done extremely carefully in production
enum EthContractType {
unknown,
erc20,
erc721;
}
enum EthContractType { unknown, erc20, erc721 }
2 changes: 2 additions & 0 deletions lib/models/isar/models/isar_models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ export 'blockchain_data/transaction.dart';
export 'blockchain_data/utxo.dart';
export 'ethereum/eth_contract.dart';
export 'log.dart';
export 'solana/sol_contract.dart';
export 'transaction_note.dart';
export '../../../wallets/isar/models/wallet_solana_token_info.dart';
62 changes: 62 additions & 0 deletions lib/models/isar/models/solana/sol_contract.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* This file is part of Stack Wallet.
*
* Copyright (c) 2025 Cypher Stack
* All Rights Reserved.
* The code is distributed under GPLv3 license, see LICENSE file for details.
*
*/

import 'package:isar_community/isar.dart';

import '../contract.dart';

part 'sol_contract.g.dart';

@collection
class SolContract extends Contract {
SolContract({
required this.address,
required this.name,
required this.symbol,
required this.decimals,
this.logoUri,
this.metadataAddress,
});

Id id = Isar.autoIncrement;

@override
@Index(unique: true, replace: true)
late final String address; // Mint address.

@override
late final String name;

@override
late final String symbol;

@override
late final int decimals;

late final String? logoUri;

late final String? metadataAddress;

SolContract copyWith({
Id? id,
String? address,
String? name,
String? symbol,
int? decimals,
String? logoUri,
String? metadataAddress,
}) => SolContract(
address: address ?? this.address,
name: name ?? this.name,
symbol: symbol ?? this.symbol,
decimals: decimals ?? this.decimals,
logoUri: logoUri ?? this.logoUri,
metadataAddress: metadataAddress ?? this.metadataAddress,
)..id = id ?? this.id;
}
Loading
Loading