Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
12 changes: 12 additions & 0 deletions FRW.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,8 @@
4E9182622EB89C63008FD73A /* AdaptiveHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9182612EB89C63008FD73A /* AdaptiveHostingController.swift */; };
4E9182632EB89C63008FD73A /* AdaptiveHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9182612EB89C63008FD73A /* AdaptiveHostingController.swift */; };
4E947C922BBEB2E300E87A85 /* web3swift in Frameworks */ = {isa = PBXBuildFile; productRef = 4E947C912BBEB2E300E87A85 /* web3swift */; };
4E952F4A2EE2B59F00175DAA /* NativeScreenName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E952F492EE2B59F00175DAA /* NativeScreenName.swift */; };
4E952F4B2EE2B59F00175DAA /* NativeScreenName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E952F492EE2B59F00175DAA /* NativeScreenName.swift */; };
4E95322F2CD2082F00AAECD1 /* AddCustomTokenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E95322E2CD2082F00AAECD1 /* AddCustomTokenView.swift */; };
4E9532302CD2082F00AAECD1 /* AddCustomTokenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E95322E2CD2082F00AAECD1 /* AddCustomTokenView.swift */; };
4E9532322CD2089C00AAECD1 /* AddCustomTokenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9532312CD2089C00AAECD1 /* AddCustomTokenViewModel.swift */; };
Expand Down Expand Up @@ -1797,6 +1799,8 @@
4EC5E06A2BD8B20D003A1A24 /* EVMModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC5E0682BD8B20D003A1A24 /* EVMModels.swift */; };
4EC626092AE8A28100318720 /* UUIDManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC626082AE8A28100318720 /* UUIDManager.swift */; };
4EC6260A2AE8A28100318720 /* UUIDManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC626082AE8A28100318720 /* UUIDManager.swift */; };
4EC758412EEAC17F00B9012E /* TurboModule+Onboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC758402EEAC16F00B9012E /* TurboModule+Onboard.swift */; };
4EC758422EEAC17F00B9012E /* TurboModule+Onboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC758402EEAC16F00B9012E /* TurboModule+Onboard.swift */; };
4EC8675D2EBAF25400AFD76D /* AuthnAccountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC8675C2EBAF25400AFD76D /* AuthnAccountsView.swift */; };
4EC8675E2EBAF25400AFD76D /* AuthnAccountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC8675C2EBAF25400AFD76D /* AuthnAccountsView.swift */; };
4EC97652286A07060066811D /* NFTCollectionEnableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC97651286A07060066811D /* NFTCollectionEnableView.swift */; };
Expand Down Expand Up @@ -3243,6 +3247,7 @@
4E8E3F882D8306C7000916A6 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = "<group>"; };
4E90592E2B1792F10029FC66 /* SyncAddDeviceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncAddDeviceView.swift; sourceTree = "<group>"; };
4E9182612EB89C63008FD73A /* AdaptiveHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveHostingController.swift; sourceTree = "<group>"; };
4E952F492EE2B59F00175DAA /* NativeScreenName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeScreenName.swift; sourceTree = "<group>"; };
4E95322E2CD2082F00AAECD1 /* AddCustomTokenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCustomTokenView.swift; sourceTree = "<group>"; };
4E9532312CD2089C00AAECD1 /* AddCustomTokenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCustomTokenViewModel.swift; sourceTree = "<group>"; };
4E9532342CD2196400AAECD1 /* SectionItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionItem.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3343,6 +3348,7 @@
4EC56B2F281FC57900246878 /* FlowQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowQuery.swift; sourceTree = "<group>"; };
4EC5E0682BD8B20D003A1A24 /* EVMModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EVMModels.swift; sourceTree = "<group>"; };
4EC626082AE8A28100318720 /* UUIDManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UUIDManager.swift; sourceTree = "<group>"; };
4EC758402EEAC16F00B9012E /* TurboModule+Onboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TurboModule+Onboard.swift"; sourceTree = "<group>"; };
4EC8675C2EBAF25400AFD76D /* AuthnAccountsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthnAccountsView.swift; sourceTree = "<group>"; };
4EC97651286A07060066811D /* NFTCollectionEnableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFTCollectionEnableView.swift; sourceTree = "<group>"; };
4ECA7E64284371CA0039A210 /* NFTBlurImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFTBlurImageView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4841,12 +4847,14 @@
4EECEB202E323F29008E21B6 /* RNEncoder.swift */,
1575765D2E322E6A00D7D8C4 /* BridgeModels.swift */,
15A87E902E26D9E100F0E550 /* TurboModuleSwift.swift */,
4EC758402EEAC16F00B9012E /* TurboModule+Onboard.swift */,
15A87E8D2E26D9CF00F0E550 /* RCTNativeFRWBridge.mm */,
1594792C2E282F7000F2B7A6 /* RCTNativeFRWBridge.h */,
4EECEB1D2E3235AB008E21B6 /* NativeToRNModel.swift */,
4E7712E02E3B2AD70011C1DA /* RNBridgeError.swift */,
4E33F7EE2E432F5D00B3D62F /* SendToConfig+Helper.swift */,
4E77CDFF2ECC84B100C2C628 /* RNBridgeWalletAccount+Helper.swift */,
4E952F492EE2B59F00175DAA /* NativeScreenName.swift */,
);
path = Bridge;
sourceTree = "<group>";
Expand Down Expand Up @@ -8539,6 +8547,7 @@
15DC21ED27819C57000B187A /* StateColors_OOID.swift in Sources */,
4E63A57C2B8D6A4100BBD15F /* MoveTokenView.swift in Sources */,
6A115972288E72CC005EF797 /* Coordinator.swift in Sources */,
4E952F4B2EE2B59F00175DAA /* NativeScreenName.swift in Sources */,
6AF2ED9228364BC200F06AD3 /* UIFont.swift in Sources */,
4E77CE2F2ECEABE000C2C628 /* WalletEmoji.swift in Sources */,
4ED041F82C7C25DF0016848A /* WalletNewsHandler.swift in Sources */,
Expand Down Expand Up @@ -9311,6 +9320,7 @@
6A95CFB228B7258100267F86 /* NFTTransferView.swift in Sources */,
15DC207727819C56000B187A /* VText.swift in Sources */,
4E9059302B1792F10029FC66 /* SyncAddDeviceView.swift in Sources */,
4EC758412EEAC17F00B9012E /* TurboModule+Onboard.swift in Sources */,
6A164F642845F1CB0026B31E /* UIView+Extensions.swift in Sources */,
4EB8223D2C040FFE0014E73D /* AccountSideCell.swift in Sources */,
15DC1D5D27816F46000B187A /* MoyaAsync.swift in Sources */,
Expand Down Expand Up @@ -9630,6 +9640,7 @@
4E63A57B2B8D6A4100BBD15F /* MoveTokenView.swift in Sources */,
6A115971288E72CC005EF797 /* Coordinator.swift in Sources */,
15C58AC42868A4EE00BD4FC6 /* UIFont.swift in Sources */,
4E952F4A2EE2B59F00175DAA /* NativeScreenName.swift in Sources */,
4ED041F72C7C25DF0016848A /* WalletNewsHandler.swift in Sources */,
4E77CE302ECEABE000C2C628 /* WalletEmoji.swift in Sources */,
4EBDF2B92BFB3A9700E56968 /* FlowModel+NFT.swift in Sources */,
Expand Down Expand Up @@ -10402,6 +10413,7 @@
15B302B62D9AD51A007C792C /* SideContainerView.swift in Sources */,
15C58BDE2868A4EE00BD4FC6 /* VText.swift in Sources */,
4E90592F2B1792F10029FC66 /* SyncAddDeviceView.swift in Sources */,
4EC758422EEAC17F00B9012E /* TurboModule+Onboard.swift in Sources */,
15C58BDF2868A4EE00BD4FC6 /* UIView+Extensions.swift in Sources */,
15C58BE12868A4EE00BD4FC6 /* MoyaAsync.swift in Sources */,
4EB8223C2C040FFE0014E73D /* AccountSideCell.swift in Sources */,
Expand Down
4 changes: 2 additions & 2 deletions FRW/Foundation/Bridge/BridgeModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ enum RNBridge {
}

enum AccountTypeType: String, Codable {
case eoa = "eoa"
case coa = "coa"
case full = "full"
case hardware = "hardware"
case null = "null"
}

Expand Down
18 changes: 18 additions & 0 deletions FRW/Foundation/Bridge/NativeScreenName.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// NativeScreenName.swift
// FRW
//
// Created by cat on 12/5/25.
//

import Foundation

enum NativeScreenName: String {
case deviceBackup
case recoveryPhraseRestore
case keyStoreRestore
case privateKeyRestore
case googleDriveRestore
case multiRestore
case backupOptions
}
86 changes: 86 additions & 0 deletions FRW/Foundation/Bridge/RCTNativeFRWBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -203,5 +203,91 @@ - (void)ethSign:(NSString *)hexData resolve:(RCTPromiseResolveBlock)resolve reje
resolve(result);
}
}
//TODO: screenName
- (void)launchNativeScreen:(NSString *)screenName params:(NSString * _Nullable)params {
[TurboModuleSwift launchNativeScreenWithScreen:screenName params:params];
}

// MARK: - Device Info
- (NSString *)getDeviceId {
return [TurboModuleSwift getDeviceId];
}

// MARK: - Notification Permissions
- (void)requestNotificationPermission:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[TurboModuleSwift requestNotificationPermissionWithCompletionHandler:^(BOOL granted, NSError * _Nullable error) {
if (error) {
reject(@"notification_permission_error", error.localizedDescription, error);
} else {
resolve(@(granted));
}
}];
}

- (void)checkNotificationPermission:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[TurboModuleSwift checkNotificationPermissionWithCompletionHandler:^(BOOL granted, NSError * _Nullable error) {
if (error) {
reject(@"notification_permission_error", error.localizedDescription, error);
} else {
resolve(@(granted));
}
}];
}

// MARK: - Screen Security
- (void)setScreenSecurityLevel:(NSString *)level {
[TurboModuleSwift setScreenSecurityLevelWithLevel:level];
}

// MARK: - Onboarding Methods
- (void)generateSeedPhrase:(NSNumber *)strength resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[TurboModuleSwift generateSeedPhraseWithStrength:strength completionHandler:^(NSDictionary<NSString *,id> * _Nullable result, NSError * _Nullable error) {
if (error) {
reject(@"seed_phrase_generation_error", error.localizedDescription, error);
} else {
resolve(result);
}
}];
}

- (void)registerSecureTypeAccount:(NSString *)username resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[TurboModuleSwift registerSecureTypeAccountWithUsername:username completionHandler:^(NSDictionary<NSString *,id> * _Nullable result, NSError * _Nullable error) {
if (error) {
reject(@"register_account_error", error.localizedDescription, error);
} else {
resolve(result);
}
}];
}

- (void)initSecureEnclaveWallet:(NSString *)txId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[TurboModuleSwift initSecureEnclaveWalletWithTxId:txId completionHandler:^(NSDictionary<NSString *,id> * _Nullable result, NSError * _Nullable error) {
if (error) {
reject(@"init_wallet_error", error.localizedDescription, error);
} else {
resolve(result);
}
}];
}

- (void)signInWithCustomToken:(NSString *)customToken resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[TurboModuleSwift signInWithCustomTokenWithCustomToken:customToken completionHandler:^(NSError * _Nullable error) {
if (error) {
reject(@"sign_in_error", error.localizedDescription, error);
} else {
resolve(nil);
}
}];
}

- (void)saveMnemonic:(NSString *)mnemonic customToken:(NSString *)customToken txId:(NSString *)txId username:(NSString *)username resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
[TurboModuleSwift saveMnemonicWithMnemonic:mnemonic customToken:customToken txId:txId username:username completionHandler:^(NSError * _Nullable error) {
if (error) {
reject(@"save_mnemonic_error", error.localizedDescription, error);
} else {
resolve(nil);
}
}];
}

@end
6 changes: 6 additions & 0 deletions FRW/Foundation/Bridge/RNBridgeError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,10 @@ enum RNBridgeError: Error {
case scanInvalidProvider
case invalidParameters
case sendToFlowConfigurationError
case mnemonicGenerationFailed
case mnemonicSaveFailed
case keyGenerationFailed
case invalidMnemonic
case accountCreationFailed
case walletInitializationFailed
}
157 changes: 157 additions & 0 deletions FRW/Foundation/Bridge/TurboModule+Onboard.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//
// TurboModule+Onboard.swift
// FRW
//
// Created by cat on 12/11/25.
//

import Foundation
import FlowWalletKit
import WalletCore
import Flow
import UserNotifications
import FirebaseAuth
import Firebase

extension TurboModuleSwift {
@objc
static func generateSeedPhrase(strength: NSNumber?) async throws -> [String: Any] {

let mnemonicStrength = strength?.int32Value ?? 128
guard let hdWallet = HDWallet(strength: mnemonicStrength, passphrase: "") else {
HUD.error(title: "invalid_data".localized)
throw RNBridgeError.mnemonicGenerationFailed
}

let key = FlowWalletKit.SeedPhraseKey(hdWallet: hdWallet, storage: FlowWalletKit.SeedPhraseKey.seedPhraseStorage)

guard let publicKey = key.publicKey(signAlgo: .ECDSA_SECP256k1) else {
throw RNBridgeError.accountCreationFailed
}
let publicKeyHex = publicKey.hexString
let accountKey = RNBridge.AccountKey(
publicKey: publicKeyHex,
hashAlgoStr: Flow.HashAlgorithm.SHA2_256.id,
signAlgoStr: Flow.SignatureAlgorithm.ECDSA_SECP256k1.id,
weight: 1000,
hashAlgo: Flow.HashAlgorithm.SHA2_256.index,
signAlgo: Flow.SignatureAlgorithm.ECDSA_SECP256k1.index
)

let response = RNBridge.SeedPhraseGenerationResponse(
mnemonic: hdWallet.mnemonic,
accountKey: accountKey,
drivepath: FlowWalletKit.SeedPhraseKey.derivationPath
)

return try response.toDictionary()
}

@objc
static func registerSecureTypeAccount(username: String) async throws -> [String: Any] {
print("TurboModuleSwift: registerSecureTypeAccount called")
do {
let result = try await UserManager.shared.register(username)
guard let txid = result else {
throw RNBridgeError.accountCreationFailed
}
let response = RNBridge.CreateAccountResponse(
success: true,
address: "",
username: username,
accountType: .hardware,
txId: txid,
error: ""
)

return try response.toDictionary()
} catch {
let response = RNBridge.CreateAccountResponse(
success: false,
address: "",
username: username,
accountType: .hardware,
txId: "",
error: error.localizedDescription
)
return try response.toDictionary()
}
}

@objc
static func initSecureEnclaveWallet(txId: String) async throws -> [String: Any] {
print("TurboModuleSwift: initSecureEnclaveWallet called")
guard let uid = UserManager.shared.RNRegisterInfo[txId] else {
return [
"success": false,
"address": "",
"error": "Secure Enclave wallet initialization not yet implemented in WalletManager"
]
}
let result = await WalletManager.shared.keyProvider(with: uid)?.keyType == .secureEnclave
let address = await WalletManager.shared.getPrimaryWalletAddress() ?? ""
return [
"success": result,
"address": result ? address : "",
"error": ""
]
}

@objc
static func signInWithCustomToken(customToken: String) async throws {
print("TurboModuleSwift: signInWithCustomToken called")
try await Auth.auth().signIn(withCustomToken: customToken)
}

@objc
static func saveMnemonic(mnemonic: String, customToken: String, txId: String, username: String) async throws {
print("TurboModuleSwift: saveMnemonic called")
try await UserManager.shared.restoreLogin(withMnemonic: mnemonic)

Comment on lines 104 to 106

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameters customToken, txId, and username are declared but never used in the method body. Either remove these unused parameters or implement the logic that requires them.

}

// MARK: - Screen Security
@objc
static func setScreenSecurityLevel(level: String) {
print("TurboModuleSwift: setScreenSecurityLevel called")
log.info("Screen Security level:\(level)")
switch level {
case "secure":
break
default:
break
}

}

// MARK: - Notification Permissions
@objc
static func requestNotificationPermission() async throws -> Bool {
print("TurboModuleSwift: requestNotificationPermission called")
return await withCheckedContinuation { continuation in
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
continuation.resume(returning: granted)
}
}
}

@objc
static func checkNotificationPermission() async throws -> Bool {
print("TurboModuleSwift: checkNotificationPermission called")
return await withCheckedContinuation { continuation in
UNUserNotificationCenter.current().getNotificationSettings { settings in
let granted = settings.authorizationStatus == .authorized
print("TurboModuleSwift: result: \(granted)")
continuation.resume(returning: granted)
}
}
}

// MARK: - Device Info
@objc
static func getDeviceId() -> String {
print("TurboModuleSwift: getDeviceId called")
return UUIDManager.appUUID()
}

}
Loading