Skip to content
Closed
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
15 changes: 15 additions & 0 deletions FRW.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@
1570467F2793EBA100D1747B /* GithubEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1570467D2793EBA100D1747B /* GithubEndpoint.swift */; };
1575765E2E322E6A00D7D8C4 /* BridgeModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575765D2E322E6A00D7D8C4 /* BridgeModels.swift */; };
1575765F2E322E6A00D7D8C4 /* BridgeModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575765D2E322E6A00D7D8C4 /* BridgeModels.swift */; };
15C0A7F12F1B123400ABCDEF /* NativeEventModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15C0A7F02F1B123400ABCDEF /* NativeEventModels.swift */; };
15C0A7F22F1B123400ABCDEF /* NativeEventModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15C0A7F02F1B123400ABCDEF /* NativeEventModels.swift */; };
1575A71428B25EE800ADC513 /* FluidView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575A71328B25EE800ADC513 /* FluidView.swift */; };
1575A71528B25EE800ADC513 /* FluidView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575A71328B25EE800ADC513 /* FluidView.swift */; };
1575A72628B26B9600ADC513 /* index.html in Resources */ = {isa = PBXBuildFile; fileRef = 1575A72128B26B9600ADC513 /* index.html */; };
Expand Down Expand Up @@ -479,7 +481,9 @@
159F7FC42E96B78600B153A2 /* AlertOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159F7FC32E96B78600B153A2 /* AlertOverlayView.swift */; };
159F7FC52E96B78600B153A2 /* AlertOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159F7FC32E96B78600B153A2 /* AlertOverlayView.swift */; };
15A87E8E2E26D9CF00F0E550 /* RCTNativeFRWBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 15A87E8D2E26D9CF00F0E550 /* RCTNativeFRWBridge.mm */; };
4203AA4AEF9F48688E5A18AF /* NativeRequestEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 74AC00DD79D444B3ACA2B5CA /* NativeRequestEventEmitter.m */; };
15A87E8F2E26D9CF00F0E550 /* RCTNativeFRWBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 15A87E8D2E26D9CF00F0E550 /* RCTNativeFRWBridge.mm */; };
F128232BA9CC4A8E8C06C0EA /* NativeRequestEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 74AC00DD79D444B3ACA2B5CA /* NativeRequestEventEmitter.m */; };
15A87E912E26D9E100F0E550 /* TurboModuleSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A87E902E26D9E100F0E550 /* TurboModuleSwift.swift */; };
15A87E922E26D9E100F0E550 /* TurboModuleSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A87E902E26D9E100F0E550 /* TurboModuleSwift.swift */; };
15ADAE2A28F51EBB0014B722 /* SymmetricEncryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15ADAE2928F51EBB0014B722 /* SymmetricEncryption.swift */; };
Expand Down Expand Up @@ -2715,6 +2719,7 @@
1570467A2793D60000D1747B /* NFTListResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFTListResponse.swift; sourceTree = "<group>"; };
1570467D2793EBA100D1747B /* GithubEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubEndpoint.swift; sourceTree = "<group>"; };
1575765D2E322E6A00D7D8C4 /* BridgeModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgeModels.swift; sourceTree = "<group>"; };
15C0A7F02F1B123400ABCDEF /* NativeEventModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeEventModels.swift; sourceTree = "<group>"; };
1575A71328B25EE800ADC513 /* FluidView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FluidView.swift; sourceTree = "<group>"; };
1575A72128B26B9600ADC513 /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = "<group>"; };
1575A72228B26B9600ADC513 /* dat.gui.min.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = dat.gui.min.js; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2760,6 +2765,8 @@
158CE8872782DAD7006A8394 /* UserResponses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserResponses.swift; sourceTree = "<group>"; };
158CE88A2782F1FA006A8394 /* RecoveryPhraseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseViewModel.swift; sourceTree = "<group>"; };
1594792C2E282F7000F2B7A6 /* RCTNativeFRWBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTNativeFRWBridge.h; sourceTree = "<group>"; };
2462FD6909FE440094144F44 /* NativeRequestEventEmitter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NativeRequestEventEmitter.h; sourceTree = "<group>"; };
74AC00DD79D444B3ACA2B5CA /* NativeRequestEventEmitter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NativeRequestEventEmitter.m; sourceTree = "<group>"; };
1596A8CF2E1AD76200E85303 /* ReactNativeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactNativeViewController.swift; sourceTree = "<group>"; };
1596A8D72E1AD9B300E85303 /* main.jsbundle */ = {isa = PBXFileReference; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
1599F76F278892680094B196 /* InputMnemonicViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputMnemonicViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4840,8 +4847,12 @@
4E33F7D42E42423F00B3D62F /* RNTokenModel.swift */,
4EECEB202E323F29008E21B6 /* RNEncoder.swift */,
1575765D2E322E6A00D7D8C4 /* BridgeModels.swift */,
15C0A7F02F1B123400ABCDEF /* NativeEventModels.swift */,
15A87E902E26D9E100F0E550 /* TurboModuleSwift.swift */,
15A87E8D2E26D9CF00F0E550 /* RCTNativeFRWBridge.mm */,
2462FD6909FE440094144F44 /* NativeRequestEventEmitter.h */,
74AC00DD79D444B3ACA2B5CA /* NativeRequestEventEmitter.m */,

1594792C2E282F7000F2B7A6 /* RCTNativeFRWBridge.h */,
4EECEB1D2E3235AB008E21B6 /* NativeToRNModel.swift */,
4E7712E02E3B2AD70011C1DA /* RNBridgeError.swift */,
Expand Down Expand Up @@ -8820,6 +8831,7 @@
15DC20B527819C56000B187A /* VChevronButtonDirection.swift in Sources */,
15DC20CF27819C56000B187A /* VNavigationLinkExtension.swift in Sources */,
15A87E8F2E26D9CF00F0E550 /* RCTNativeFRWBridge.mm in Sources */,
F128232BA9CC4A8E8C06C0EA /* NativeRequestEventEmitter.m in Sources */,
15DC212727819C56000B187A /* VSpinnerModelContinous.swift in Sources */,
6AF95B3D2848686D0012C837 /* AddressBookRequest.swift in Sources */,
4E7153212E9752D700C5D0BA /* DeleteSEKeychain.swift in Sources */,
Expand Down Expand Up @@ -8967,6 +8979,7 @@
15DC20BD27819C56000B187A /* VSquareButtonModel.swift in Sources */,
4EC482D3284A3D75000A7E1B /* MMCQ.swift in Sources */,
1575765F2E322E6A00D7D8C4 /* BridgeModels.swift in Sources */,
15C0A7F22F1B123400ABCDEF /* NativeEventModels.swift in Sources */,
15DC21AB27819C56000B187A /* FocusableTextField.swift in Sources */,
15DC219B27819C56000B187A /* VTextFieldActions.swift in Sources */,
6A98509C28C71B5D00A07C2D /* BrowserAuthnViewModel.swift in Sources */,
Expand Down Expand Up @@ -9911,6 +9924,7 @@
15C58B0D2868A4EE00BD4FC6 /* VNavigationLinkExtension.swift in Sources */,
15C58B0E2868A4EE00BD4FC6 /* VSpinnerModelContinous.swift in Sources */,
15A87E8E2E26D9CF00F0E550 /* RCTNativeFRWBridge.mm in Sources */,
4203AA4AEF9F48688E5A18AF /* NativeRequestEventEmitter.m in Sources */,
15C58B0F2868A4EE00BD4FC6 /* AddressBookRequest.swift in Sources */,
15C58B102868A4EE00BD4FC6 /* VMenuRow.swift in Sources */,
4E7153222E9752D700C5D0BA /* DeleteSEKeychain.swift in Sources */,
Expand Down Expand Up @@ -10058,6 +10072,7 @@
15C58B512868A4EE00BD4FC6 /* FocusableTextField.swift in Sources */,
15C58B522868A4EE00BD4FC6 /* VTextFieldActions.swift in Sources */,
1575765E2E322E6A00D7D8C4 /* BridgeModels.swift in Sources */,
15C0A7F12F1B123400ABCDEF /* NativeEventModels.swift in Sources */,
6A98509B28C71B5D00A07C2D /* BrowserAuthnViewModel.swift in Sources */,
6A73BF5228C59028004B836A /* JSMessageHandler.swift in Sources */,
4EE6FA892B19677B006A827B /* SyncConfirmViewModel.swift in Sources */,
Expand Down
38 changes: 38 additions & 0 deletions FRW/Foundation/Bridge/NativeEventModels.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// NativeEventModels.swift
// FRW
//
// Auto-generated from TypeScript bridge types
// Do not edit manually
//

import Foundation

enum RNNativeEvent {
struct KeyRotationCheckParams: Codable {
let address: String
}

struct KeyRotationCheckResult: Codable {
let address: String
let isBlocto: Bool
}

struct NativeRequestPayload: Codable {
let requestId: String
let eventName: NativeEventName
let paramsJson: String
}

struct NativeResponsePayload: Codable {
let requestId: String
let eventName: NativeEventName
let resultJson: String?
let error: String?
}

enum NativeEventName: String, Codable {
case keyrotationcheck = "keyRotationCheck"
}

}
5 changes: 5 additions & 0 deletions FRW/Foundation/Bridge/NativeRequestEventEmitter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import <React/RCTEventEmitter.h>
#import <React/RCTBridgeModule.h>

@interface NativeRequestEventEmitter : RCTEventEmitter <RCTBridgeModule>
@end
51 changes: 51 additions & 0 deletions FRW/Foundation/Bridge/NativeRequestEventEmitter.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#import "NativeRequestEventEmitter.h"

@interface NativeRequestEventEmitter ()
@property (nonatomic, assign) BOOL hasListeners;
@end

@implementation NativeRequestEventEmitter

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents {
return @[ @"nativeRequest" ];
}

- (void)startObserving {
self.hasListeners = YES;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNativeRequest:)
name:@"nativeRequest"
object:nil];
}

- (void)stopObserving {
self.hasListeners = NO;
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"nativeRequest"
object:nil];
}

- (void)invalidate {
[super invalidate];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)handleNativeRequest:(NSNotification *)notification {
if (!self.hasListeners) {
return;
}

NSDictionary *userInfo = [notification.userInfo isKindOfClass:[NSDictionary class]]
? notification.userInfo
: @{};

if (userInfo.count == 0) {
return;
}

[self sendEventWithName:@"nativeRequest" body:userInfo];
}

@end
52 changes: 52 additions & 0 deletions FRW/Foundation/Bridge/RCTNativeFRWBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,58 @@ - (void)sign:(nonnull NSString *)hexData
}];
}

- (void)signRotationRequest:(nonnull NSString *)publicKey
address:(nonnull NSString *)address
hash:(nonnull NSString *)hash
resolve:(nonnull RCTPromiseResolveBlock)resolve
reject:(nonnull RCTPromiseRejectBlock)reject {
[TurboModuleSwift signRotationRequestWithPublicKey:publicKey
address:address
hash:hash
completionHandler:^(NSString *_Nullable signature,
NSError *_Nullable error) {
if (error) {
reject(@"sign_rotation_error", error.localizedDescription, error);
} else {
resolve(signature);
}
}];
}

- (void)removeOldKey:(nonnull NSString *)address
publicKey:(nonnull NSString *)publicKey
resolve:(nonnull RCTPromiseResolveBlock)resolve
reject:(nonnull RCTPromiseRejectBlock)reject {
[TurboModuleSwift removeOldKeyWithAddress:address
publicKey:publicKey
completionHandler:^(NSError *_Nullable error) {
if (error) {
reject(@"remove_old_key_error", error.localizedDescription, error);
} else {
resolve(nil);
}
}];
}

- (void)nativeResponse:(nonnull NSString *)requestId
eventName:(nonnull NSString *)eventName
resultJson:(NSString * _Nullable)resultJson
error:(NSString * _Nullable)error
resolve:(nonnull RCTPromiseResolveBlock)resolve
reject:(nonnull RCTPromiseRejectBlock)reject {
[TurboModuleSwift nativeResponseWithRequestId:requestId
eventName:eventName
resultJson:resultJson
error:error
completionHandler:^(NSError *_Nullable nativeError) {
if (nativeError) {
reject(@"native_response_error", nativeError.localizedDescription, nativeError);
} else {
resolve(nil);
}
}];
}

- (NSNumber *)getSignKeyIndex {
return @([TurboModuleSwift getSignKeyIndex]);
}
Expand Down
33 changes: 33 additions & 0 deletions FRW/Foundation/Bridge/TurboModuleSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,39 @@ extension TurboModuleSwift {
"INSTABUG_TOKEN": ServiceConfig.instabugRNToken,
]
}

@objc
static func signRotationRequest(publicKey: String, address: String, hash: String) async throws -> String {
_ = publicKey
_ = address
let data = Data(hash.utf8)
return try await WalletManager.shared.sign(signableData: data).hexString
}

@objc
static func removeOldKey(address: String, publicKey: String) async throws {
_ = address
_ = publicKey
}
Comment on lines +164 to +167
Copy link

Choose a reason for hiding this comment

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

The removeOldKey function accepts address and publicKey parameters but does nothing with them. This appears to be a stub implementation that should either be completed or documented as a placeholder.


@objc
static func nativeResponse(
requestId: String,
eventName: String,
resultJson: String?,
error: String?
) async throws {
NotificationCenter.default.post(
name: .nativeResponse,
object: nil,
userInfo: [
"requestId": requestId,
"eventName": eventName,
"resultJson": resultJson ?? "",
"error": error ?? "",
]
)
}
}

// MARK: - React Native Management
Expand Down
2 changes: 2 additions & 0 deletions FRW/Foundation/Define/NotificationDefine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,6 @@ public extension Notification.Name {
static let accountDataDidUpdate = Notification.Name("accountDataDidUpdate")
static let dropboxCallback = Notification.Name("multiBack.dropboxCallback")
static let hiddenAddressesDidChanged = Notification.Name("hiddenAddressesDidChanged")
static let nativeRequest = Notification.Name("nativeRequest")
static let nativeResponse = Notification.Name("nativeResponse")
}
55 changes: 55 additions & 0 deletions FRW/Services/Manager/Wallet/WalletManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ class WalletManager: ObservableObject {
name: .willResetWallet,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleNativeResponse(_:)),
name: .nativeResponse,
object: nil
)
self.currentNetwork = LocalUserDefaults.shared.network
flow.configure(chainID: currentNetwork)
start()
Expand Down Expand Up @@ -437,6 +443,8 @@ extension WalletManager {
mainAccount = account
loadLinkedAccounts()
}

notifyKeyRotationAddressChanged(address: address)
}

func switchSelectedAccount(_ selectingAccount: WalletAccount) {
Expand Down Expand Up @@ -472,6 +480,8 @@ extension WalletManager {
break

}

notifyKeyRotationAddressChanged(address: selectingAccount.address)
}

func changeNetwork(_ network: Flow.ChainID) {
Expand Down Expand Up @@ -505,6 +515,51 @@ extension WalletManager {
}
}

// MARK: - React Native Key Rotation Hook

extension WalletManager {
private func notifyKeyRotationAddressChanged(address: String) {
guard !address.isEmpty else { return }
let paramsJson: String = {
let payload = ["address": address]
guard let data = try? JSONSerialization.data(withJSONObject: payload),
let json = String(data: data, encoding: .utf8) else {
return "{}"
}
return json
}()
// React Native should observe this notification and trigger KeyRotationService.
NotificationCenter.default.post(
name: .nativeRequest,
object: nil,
userInfo: [
"requestId": UUID().uuidString,
"eventName": "keyRotationCheck",
"paramsJson": paramsJson,
]
)
log.debug("[WalletManager] Posted key rotation address change: \(address)")
}

@objc
private func handleNativeResponse(_ notification: Notification) {
guard let info = notification.userInfo else { return }
let requestId = info["requestId"] as? String ?? ""
let eventName = info["eventName"] as? String ?? ""
let resultJson = info["resultJson"] as? String ?? ""
let error = info["error"] as? String
log.debug(
"[WalletManager] Native response",
[
"requestId": requestId,
"eventName": eventName,
"resultJson": resultJson,
"error": error ?? "",
]
)
}
}

// MARK: - account type

extension WalletManager {
Expand Down