diff --git a/Content/BlueprintSampleContent/PassportFeaturesWidget4_26.uasset b/Content/BlueprintSampleContent/PassportFeaturesWidget4_26.uasset index 2ab066cb..682e2533 100644 Binary files a/Content/BlueprintSampleContent/PassportFeaturesWidget4_26.uasset and b/Content/BlueprintSampleContent/PassportFeaturesWidget4_26.uasset differ diff --git a/Content/BlueprintSampleContent/PassportLoginWidget4_26.uasset b/Content/BlueprintSampleContent/PassportLoginWidget4_26.uasset index ca6926fe..457eb7e1 100644 Binary files a/Content/BlueprintSampleContent/PassportLoginWidget4_26.uasset and b/Content/BlueprintSampleContent/PassportLoginWidget4_26.uasset differ diff --git a/Content/ImtblSampleLevel4_26.umap b/Content/ImtblSampleLevel4_26.umap index 7b11615d..a9e9bae1 100644 Binary files a/Content/ImtblSampleLevel4_26.umap and b/Content/ImtblSampleLevel4_26.umap differ diff --git a/Content/PackagedResources/index.uasset b/Content/PackagedResources/index.uasset index 21de0eb0..e1c12e1c 100644 Binary files a/Content/PackagedResources/index.uasset and b/Content/PackagedResources/index.uasset differ diff --git a/Source/Immutable/Immutable.Build.cs b/Source/Immutable/Immutable.Build.cs index 9bb9c4da..27b42bca 100644 --- a/Source/Immutable/Immutable.Build.cs +++ b/Source/Immutable/Immutable.Build.cs @@ -79,6 +79,11 @@ public Immutable(ReadOnlyTargetRules Target) : base(Target) // ... add any modules that your module loads dynamically here ... } ); + + if (Target.bBuildEditor == true) + { + PrivateDependencyModuleNames.Add("UnrealEd"); + } if (Target.Platform == UnrealTargetPlatform.Android) { diff --git a/Source/Immutable/Immutable_UPL_Android.xml b/Source/Immutable/Immutable_UPL_Android.xml index bbcebe6c..ea9a9cf6 100644 --- a/Source/Immutable/Immutable_UPL_Android.xml +++ b/Source/Immutable/Immutable_UPL_Android.xml @@ -55,11 +55,11 @@ public native void handleDeepLink(String Deeplink); - public native void handleOnCustomTabsDismissed(); + public native void handleOnCustomTabsDismissed(String Url); @Override - public void onCustomTabsDismissed() { - handleOnCustomTabsDismissed(); + public void onCustomTabsDismissed(String Url) { + handleOnCustomTabsDismissed(Url); } diff --git a/Source/Immutable/Private/Immutable/Actions/ImtblPassportConnectSilentAsyncAction.cpp b/Source/Immutable/Private/Immutable/Actions/ImtblPassportConnectSilentAsyncAction.cpp new file mode 100644 index 00000000..91762cb4 --- /dev/null +++ b/Source/Immutable/Private/Immutable/Actions/ImtblPassportConnectSilentAsyncAction.cpp @@ -0,0 +1,53 @@ + +#include "Immutable/Actions/ImtblPassportConnectSilentAsyncAction.h" + +#include "Immutable/ImmutablePassport.h" +#include "Immutable/ImmutableSubsystem.h" +#include "Immutable/Misc/ImtblLogging.h" + + +UImtblPassportConnectSilentAsyncAction * UImtblPassportConnectSilentAsyncAction::ConnectSilent(UObject *WorldContextObject) +{ + UImtblPassportConnectSilentAsyncAction *PassportInitBlueprintNode = NewObject(); + + PassportInitBlueprintNode->WorldContextObject = WorldContextObject; + + return PassportInitBlueprintNode; +} + +void UImtblPassportConnectSilentAsyncAction::Activate() +{ + if (!WorldContextObject || !WorldContextObject->GetWorld()) + { + FString Err = "Reconnect failed due to missing world or world context object."; + IMTBL_WARN("%s", *Err) + OnFailure.Broadcast(Err); + return; + } + + GetSubsystem()->WhenReady(this, &UImtblPassportConnectSilentAsyncAction:: DoConnectSilent); +} + +void UImtblPassportConnectSilentAsyncAction::DoConnectSilent(TWeakObjectPtr JSConnector) +{ + auto Passport = GetSubsystem()->GetPassport(); + + if (Passport.IsValid()) + { + Passport->ConnectSilent(UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject(this, &UImtblPassportConnectSilentAsyncAction::OnConnectSilentResponse)); + } +} + +void UImtblPassportConnectSilentAsyncAction::OnConnectSilentResponse(FImmutablePassportResult Result) +{ + if (Result.Success) + { + IMTBL_LOG("Reconnect success") + OnSuccess.Broadcast(Result.Message); + } + else + { + IMTBL_LOG("Reconnect failed") + OnFailure.Broadcast(Result.Message); + } +} diff --git a/Source/Immutable/Private/Immutable/Actions/ImtblPassportInitializationAsyncAction.cpp b/Source/Immutable/Private/Immutable/Actions/ImtblPassportInitializationAsyncAction.cpp index d7c960f0..3f07dc70 100644 --- a/Source/Immutable/Private/Immutable/Actions/ImtblPassportInitializationAsyncAction.cpp +++ b/Source/Immutable/Private/Immutable/Actions/ImtblPassportInitializationAsyncAction.cpp @@ -4,49 +4,50 @@ #include "Immutable/ImmutablePassport.h" #include "Immutable/ImmutableSubsystem.h" -#include "Immutable/Misc/ImtblLogging.h" - -UImtblPassportInitializationAsyncAction * -UImtblPassportInitializationAsyncAction::InitializePassport( - UObject *WorldContextObject, const FString &ClientID, - const FString &RedirectUri, const FString &Environment) { - UImtblPassportInitializationAsyncAction *PassportInitBlueprintNode = - NewObject(); - PassportInitBlueprintNode->ClientId = ClientID; - PassportInitBlueprintNode->RedirectUri = RedirectUri; - PassportInitBlueprintNode->Environment = Environment; - PassportInitBlueprintNode->WorldContextObject = WorldContextObject; - return PassportInitBlueprintNode; + + +UImtblPassportInitializationAsyncAction* UImtblPassportInitializationAsyncAction::InitializePassport(UObject* WorldContextObject, const FString& ClientID, const FString& RedirectUri, + const FString& LogoutUri, const FString& Environment) +{ + UImtblPassportInitializationAsyncAction* PassportInitBlueprintNode = NewObject(); + + PassportInitBlueprintNode->ClientId = ClientID; + PassportInitBlueprintNode->RedirectUri = RedirectUri; + PassportInitBlueprintNode->LogoutUri = LogoutUri; + PassportInitBlueprintNode->Environment = Environment; + PassportInitBlueprintNode->WorldContextObject = WorldContextObject; + + return PassportInitBlueprintNode; } -void UImtblPassportInitializationAsyncAction::Activate() { - if (!WorldContextObject || !WorldContextObject->GetWorld()) { - Failed.Broadcast( - "Initialization failed due to missing world or world context object."); - return; - } +void UImtblPassportInitializationAsyncAction::Activate() +{ + if (!WorldContextObject || !WorldContextObject->GetWorld()) + { + Failed.Broadcast("Initialization failed due to missing world or world context object."); + return; + } - GetSubsystem()->WhenReady( - this, &UImtblPassportInitializationAsyncAction::DoInit); //, /* timoutSec - //*/ 15.0f); + GetSubsystem()->WhenReady(this, &UImtblPassportInitializationAsyncAction::DoInit); } -void UImtblPassportInitializationAsyncAction::DoInit( - TWeakObjectPtr JSConnector) { - // Get Passport - auto Passport = GetSubsystem()->GetPassport(); - // Run Initialize - Passport->Initialize( - FImmutablePassportInitData{ClientId, RedirectUri, Environment}, - UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject( - this, &UImtblPassportInitializationAsyncAction::OnInitialized)); +void UImtblPassportInitializationAsyncAction::DoInit(TWeakObjectPtr JSConnector) +{ + // Get Passport + auto Passport = GetSubsystem()->GetPassport(); + // Run Initialize + Passport->Initialize(FImmutablePassportInitData{ ClientId, RedirectUri, LogoutUri, Environment }, + UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject(this, &UImtblPassportInitializationAsyncAction::OnInitialized)); } -void UImtblPassportInitializationAsyncAction::OnInitialized( - FImmutablePassportResult Result) { - if (Result.Success) { - Initialized.Broadcast(Result.Message); - } else { - Failed.Broadcast(Result.Message); - } +void UImtblPassportInitializationAsyncAction::OnInitialized(FImmutablePassportResult Result) +{ + if (Result.Success) + { + Initialized.Broadcast(Result.Message); + } + else + { + Failed.Broadcast(Result.Message); + } } diff --git a/Source/Immutable/Private/Immutable/Actions/ImtblPassportLogoutAsyncAction.cpp b/Source/Immutable/Private/Immutable/Actions/ImtblPassportLogoutAsyncAction.cpp index c3dc2f4e..a606a379 100644 --- a/Source/Immutable/Private/Immutable/Actions/ImtblPassportLogoutAsyncAction.cpp +++ b/Source/Immutable/Private/Immutable/Actions/ImtblPassportLogoutAsyncAction.cpp @@ -6,42 +6,44 @@ #include "Immutable/ImmutableSubsystem.h" #include "Immutable/Misc/ImtblLogging.h" -UImtblPassportLogoutAsyncAction * -UImtblPassportLogoutAsyncAction::Logout(UObject *WorldContextObject) { - UImtblPassportLogoutAsyncAction *PassportInitBlueprintNode = - NewObject(); - PassportInitBlueprintNode->WorldContextObject = WorldContextObject; - return PassportInitBlueprintNode; +UImtblPassportLogoutAsyncAction* UImtblPassportLogoutAsyncAction::Logout(UObject* WorldContextObject) +{ + UImtblPassportLogoutAsyncAction* PassportInitBlueprintNode = NewObject(); + + PassportInitBlueprintNode->WorldContextObject = WorldContextObject; + + return PassportInitBlueprintNode; } -void UImtblPassportLogoutAsyncAction::Activate() { - if (!WorldContextObject || !WorldContextObject->GetWorld()) { - FString Err = "Logout failed due to missing world or world context object."; - IMTBL_WARN("%s", *Err) - Failed.Broadcast(Err); - return; - } - - GetSubsystem()->WhenReady( - this, - &UImtblPassportLogoutAsyncAction::DoLogout); //, /* timoutSec */ 15.0f); +void UImtblPassportLogoutAsyncAction::Activate() +{ + if (!WorldContextObject || !WorldContextObject->GetWorld()) + { + FString Err = "Logout failed due to missing world or world context object."; + IMTBL_WARN("%s", *Err) + OnFailure.Broadcast(Err); + return; + } + + GetSubsystem()->WhenReady(this, &UImtblPassportLogoutAsyncAction::DoLogout); //, /* timoutSec */ 15.0f); } -void UImtblPassportLogoutAsyncAction::DoLogout( - TWeakObjectPtr JSConnector) { - // Get Passport - auto Passport = GetSubsystem()->GetPassport(); - // Run Logout - Passport->Logout( - UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject( - this, &UImtblPassportLogoutAsyncAction::OnLogoutResponse)); +void UImtblPassportLogoutAsyncAction::DoLogout(TWeakObjectPtr JSConnector) +{ + // Get Passport + auto Passport = GetSubsystem()->GetPassport(); + // Run Logout + Passport->Logout(UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject(this, &UImtblPassportLogoutAsyncAction::OnLogoutResponse)); } -void UImtblPassportLogoutAsyncAction::OnLogoutResponse( - FImmutablePassportResult Result) { - if (Result.Success) { - LoggedOut.Broadcast(Result.Message); - } else { - Failed.Broadcast(Result.Message); - } +void UImtblPassportLogoutAsyncAction::OnLogoutResponse(FImmutablePassportResult Result) const +{ + if (Result.Success) + { + OnSuccess.Broadcast(Result.Message); + } + else + { + OnFailure.Broadcast(Result.Message); + } } diff --git a/Source/Immutable/Private/Immutable/Actions/ImtblPassportReconnectAsyncAction.cpp b/Source/Immutable/Private/Immutable/Actions/ImtblPassportReconnectAsyncAction.cpp deleted file mode 100644 index 3a6d2dbc..00000000 --- a/Source/Immutable/Private/Immutable/Actions/ImtblPassportReconnectAsyncAction.cpp +++ /dev/null @@ -1,53 +0,0 @@ - -#include "Immutable/Actions/ImtblPassportReconnectAsyncAction.h" - -#include "Immutable/ImmutablePassport.h" -#include "Immutable/ImmutableSubsystem.h" -#include "Immutable/Misc/ImtblLogging.h" - - -UImtblPassportReconnectAsyncAction * UImtblPassportReconnectAsyncAction::Reconnect(UObject *WorldContextObject) -{ - UImtblPassportReconnectAsyncAction *PassportInitBlueprintNode = NewObject(); - - PassportInitBlueprintNode->WorldContextObject = WorldContextObject; - - return PassportInitBlueprintNode; -} - -void UImtblPassportReconnectAsyncAction::Activate() -{ - if (!WorldContextObject || !WorldContextObject->GetWorld()) - { - FString Err = "Reconnect failed due to missing world or world context object."; - IMTBL_WARN("%s", *Err) - OnFailure.Broadcast(Err); - return; - } - - GetSubsystem()->WhenReady(this, &UImtblPassportReconnectAsyncAction:: DoReconnect); -} - -void UImtblPassportReconnectAsyncAction::DoReconnect(TWeakObjectPtr JSConnector) -{ - auto Passport = GetSubsystem()->GetPassport(); - - if (Passport.IsValid()) - { - Passport->Reconnect(UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject(this, &UImtblPassportReconnectAsyncAction::OnReconnectResponse)); - } -} - -void UImtblPassportReconnectAsyncAction::OnReconnectResponse(FImmutablePassportResult Result) -{ - if (Result.Success) - { - IMTBL_LOG("Reconnect success") - OnSuccess.Broadcast(Result.Message); - } - else - { - IMTBL_LOG("Reconnect failed") - OnFailure.Broadcast(Result.Message); - } -} diff --git a/Source/Immutable/Private/Immutable/Android/ImmutableAndroidJNI.cpp b/Source/Immutable/Private/Immutable/Android/ImmutableAndroidJNI.cpp index 7b7fa25d..7ca3a033 100644 --- a/Source/Immutable/Private/Immutable/Android/ImmutableAndroidJNI.cpp +++ b/Source/Immutable/Private/Immutable/Android/ImmutableAndroidJNI.cpp @@ -1,23 +1,71 @@ #include "ImmutableAndroidJNI.h" -#include "Immutable/ImmutablePassport.h" #if PLATFORM_ANDROID -JNI_METHOD void -Java_com_epicgames_unreal_GameActivity_handleDeepLink(JNIEnv *env, jobject obj, - jstring jDeeplink) { - if (env->IsSameObject(jDeeplink, NULL)) { - return; - } - - const char *deeplinkCStr = env->GetStringUTFChars(jDeeplink, NULL); - const FString deeplink = FString(UTF8_TO_TCHAR(deeplinkCStr)); - UImmutablePassport::HandleDeepLink(deeplink); - env->ReleaseStringUTFChars(jDeeplink, deeplinkCStr); + +#include "Immutable/ImmutablePassport.h" +#include "Immutable/ImmutableSubsystem.h" +#include "Engine/GameEngine.h" + +UImmutablePassport* GetPassport() +{ + UGameEngine* GameEngine = Cast(GEngine); + + if (!GameEngine) + { + return nullptr; + } + + UWorld* World = GameEngine ? GameEngine->GetGameWorld() : NULL; + + if (!World) + { + return nullptr; + } + + auto ImmutableSubsystem = World->GetGameInstance()->GetSubsystem(); + + if (!ImmutableSubsystem) + { + return nullptr; + } + + auto Passport = ImmutableSubsystem->GetPassport(); + + if (!Passport.IsValid()) + { + return nullptr; + } + + return Passport.Get(); +} + +JNI_METHOD void Java_com_epicgames_unreal_GameActivity_handleDeepLink(JNIEnv *env, jobject obj, jstring jDeeplink) +{ + if (env->IsSameObject(jDeeplink, NULL)) + { + return; + } + + const char *deeplinkCStr = env->GetStringUTFChars(jDeeplink, NULL); + const FString deeplink = FString(UTF8_TO_TCHAR(deeplinkCStr)); + + if (auto Passport = GetPassport()) + { + Passport->HandleDeepLink(deeplink); + } + env->ReleaseStringUTFChars(jDeeplink, deeplinkCStr); } -JNI_METHOD void -Java_com_epicgames_unreal_GameActivity_handleOnCustomTabsDismissed( - JNIEnv *env, jobject obj) { - UImmutablePassport::HandleCustomTabsDismissed(); +JNI_METHOD void Java_com_epicgames_unreal_GameActivity_handleOnCustomTabsDismissed(JNIEnv *env, jobject obj, jstring jUrl) +{ + if (env->IsSameObject(jUrl, NULL)) + { + return; + } + + if (auto Passport = GetPassport()) + { + Passport->HandleCustomTabsDismissed(FString(UTF8_TO_TCHAR(env->GetStringUTFChars(jUrl, NULL)))); + } } #endif \ No newline at end of file diff --git a/Source/Immutable/Private/Immutable/Android/ImmutableAndroidJNI.h b/Source/Immutable/Private/Immutable/Android/ImmutableAndroidJNI.h index 386cd50c..8f21b095 100644 --- a/Source/Immutable/Private/Immutable/Android/ImmutableAndroidJNI.h +++ b/Source/Immutable/Private/Immutable/Android/ImmutableAndroidJNI.h @@ -3,14 +3,11 @@ #if PLATFORM_ANDROID #include "Android/AndroidJNI.h" -extern "C" { -JNI_METHOD void Java_com_epicgames_unreal_GameActivity_handleDeepLink(JNIEnv *, - jobject, - jstring); +extern "C" +{ +JNI_METHOD void Java_com_epicgames_unreal_GameActivity_handleDeepLink(JNIEnv *, jobject, jstring); -JNI_METHOD void -Java_com_epicgames_unreal_GameActivity_handleOnCustomTabsDismissed(JNIEnv *, - jobject); +JNI_METHOD void Java_com_epicgames_unreal_GameActivity_handleOnCustomTabsDismissed(JNIEnv *, jobject, jstring); } #endif diff --git a/Source/Immutable/Private/Immutable/Android/Java/ImmutableAndroid.java b/Source/Immutable/Private/Immutable/Android/Java/ImmutableAndroid.java index 0d6546c5..378648e6 100644 --- a/Source/Immutable/Private/Immutable/Android/Java/ImmutableAndroid.java +++ b/Source/Immutable/Private/Immutable/Android/Java/ImmutableAndroid.java @@ -43,7 +43,7 @@ private static int getCustomTabsHeight(Activity context) { } } - public static void launchUrl(Activity context, String url) { + public static void launchUrl(final Activity context, final String url) { // Get all apps that can support Custom Tabs Service // i.e. services that can handle ACTION_CUSTOM_TABS_CONNECTION intents PackageManager packageManager = context.getPackageManager(); @@ -84,7 +84,7 @@ public void onNavigationEvent(int navigationEvent, @Nullable Bundle extras) { handler.postDelayed(new Runnable() { @Override public void run() { - ((Callback) context).onCustomTabsDismissed(); + ((Callback) context).onCustomTabsDismissed(url); } }, 1000); } @@ -107,6 +107,6 @@ public void run() { } public interface Callback { - void onCustomTabsDismissed(); + void onCustomTabsDismissed(String url); } } \ No newline at end of file diff --git a/Source/Immutable/Private/Immutable/IOS/ImmutableIOS.cpp b/Source/Immutable/Private/Immutable/IOS/ImmutableIOS.cpp index 6043284a..85fdfb89 100644 --- a/Source/Immutable/Private/Immutable/IOS/ImmutableIOS.cpp +++ b/Source/Immutable/Private/Immutable/IOS/ImmutableIOS.cpp @@ -1,5 +1,7 @@ #include "ImmutableIOS.h" #include "Immutable/ImmutablePassport.h" +#include "Immutable/ImmutableSubsystem.h" +#include "Engine/GameEngine.h" API_AVAILABLE(ios(12.0)) ASWebAuthenticationSession *_authSession; @@ -20,6 +22,34 @@ ASWebAuthenticationSession *_authSession; return staticImmutableIOS; } ++ (UImmutablePassport*) getPassport { + UGameEngine* GameEngine = Cast(GEngine); + + if (!GameEngine) { + return nil; + } + + UWorld* World = GameEngine ? GameEngine->GetGameWorld() : NULL; + + if (!World) { + return nil; + } + + auto ImmutableSubsystem = World->GetGameInstance()->GetSubsystem(); + + if (!ImmutableSubsystem) { + return nil; + } + + auto Passport = ImmutableSubsystem->GetPassport(); + + if (!Passport.IsValid()) { + return nil; + } + + return Passport.Get(); +} + - (void)launchUrl:(const char *)url { NSURL *URL = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]]; NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; @@ -31,7 +61,11 @@ ASWebAuthenticationSession *_authSession; NSError *_Nullable error) { _authSession = nil; if (callbackURL) { - UImmutablePassport::HandleDeepLink(callbackURL.absoluteString); + UImmutablePassport* passport = [ImmutableIOS getPassport]; + + if (passport) { + passport->HandleDeepLink(callbackURL.absoluteString); + } } else { return; } diff --git a/Source/Immutable/Private/Immutable/ImmutablePassport.cpp b/Source/Immutable/Private/Immutable/ImmutablePassport.cpp index 2c59541f..5959be6c 100644 --- a/Source/Immutable/Private/Immutable/ImmutablePassport.cpp +++ b/Source/Immutable/Private/Immutable/ImmutablePassport.cpp @@ -5,6 +5,8 @@ #include "Immutable/Misc/ImtblLogging.h" #include "ImtblJSConnector.h" #include "JsonObjectConverter.h" +#include "Policies/CondensedJsonPrintPolicy.h" + #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC #include "GenericPlatform/GenericPlatformHttp.h" @@ -20,427 +22,467 @@ #include "Mac/ImmutableMac.h" #endif -FString FImmutablePassportInitData::ToJsonString() const { - FString OutString; - - FJsonObjectWrapper Wrapper; - Wrapper.JsonObject = MakeShared(); - FJsonObjectConverter::UStructToJsonObject( - FImmutablePassportInitData::StaticStruct(), this, - Wrapper.JsonObject.ToSharedRef(), 0, 0); - - if (!Wrapper.JsonObject.IsValid()) { - IMTBL_ERR("Could not convert FImmutablePassportInitData to JSON") - return ""; - } - // Remove redirectUri field if it's empty so that the bridge doesn't try to - // use it - if (Wrapper.JsonObject->HasField("redirectUri") && - Wrapper.JsonObject->GetStringField("redirectUri").IsEmpty()) - Wrapper.JsonObject->RemoveField("redirectUri"); - Wrapper.JsonObjectToString(OutString); - - return OutString; -} - -FString FImxBatchNftTransferRequest::ToJsonString() const { - FString OutString; - FJsonObjectWrapper Wrapper; - Wrapper.JsonObject = MakeShared(); - FJsonObjectConverter::UStructToJsonObject( - FImxBatchNftTransferRequest::StaticStruct(), this, - Wrapper.JsonObject.ToSharedRef(), 0, 0); - - if (!Wrapper.JsonObject.IsValid()) { - IMTBL_ERR("Could not convert FImxBatchNftTransferRequest to JSON") - return ""; - } - - if (Wrapper.JsonObject->HasField("nftTransferDetails")) { - const auto Writer = - TJsonWriterFactory>::Create( - &OutString); - FJsonSerializer::Serialize( - Wrapper.JsonObject->GetArrayField("nftTransferDetails"), Writer); - IMTBL_LOG("FImxBatchNftTransferRequest Serialised: %s", *OutString); - Writer->Close(); - } - return OutString; -} - -TOptional -FImmutablePassportConnectData::FromJsonString(const FString &JsonObjectString) { - FImmutablePassportConnectData PassportConnect; - if (!FJsonObjectConverter::JsonObjectStringToUStruct( - JsonObjectString, &PassportConnect, 0, 0)) { - IMTBL_WARN("Could not parse response from JavaScript into the expected " - "Passport connect format") - return TOptional(); - } - return PassportConnect; -} - -FString FImmutablePassportZkEvmRequestAccountsData::ToJsonString() const { - FString OutString; - FJsonObjectConverter::UStructToJsonObjectString(*this, OutString, 0, 0, 0, - nullptr, false); - return OutString; -} - -TOptional -FImmutablePassportZkEvmRequestAccountsData::FromJsonString( - const FString &JsonObjectString) { - FImmutablePassportZkEvmRequestAccountsData RequestAccounts; - if (!FJsonObjectConverter::JsonObjectStringToUStruct( - JsonObjectString, &RequestAccounts, 0, 0)) { - IMTBL_WARN("Could not parse response from JavaScript into the expected " - "Passport ZkEvm request accounts format") - return TOptional(); - } - return RequestAccounts; -} - -TOptional -FImmutablePassportZkEvmRequestAccountsData::FromJsonObject( - const TSharedPtr &JsonObject) { - if (!JsonObject.IsValid()) - return TOptional(); - - // Parse the JSON - FImmutablePassportZkEvmRequestAccountsData RequestAccounts; - if (!FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), - &RequestAccounts, 0, 0)) { - IMTBL_ERR("Could not parse response from JavaScript into the expected " - "Passport ZkEvm request accounts format") - return TOptional(); - } - return RequestAccounts; -} - -FString FImmutablePassportZkEvmGetBalanceData::ToJsonString() const { - FString OutString; - - FJsonObjectWrapper Wrapper; - Wrapper.JsonObject = MakeShared(); - FJsonObjectConverter::UStructToJsonObject( - FImmutablePassportZkEvmGetBalanceData::StaticStruct(), this, - Wrapper.JsonObject.ToSharedRef(), 0, 0); - - if (!Wrapper.JsonObject.IsValid()) { - IMTBL_ERR("Could not convert FImmutablePassportZkEvmGetBalanceData to JSON") - return ""; - } - Wrapper.JsonObjectToString(OutString); - - return OutString; +FString FImmutablePassportInitData::ToJsonString() const +{ + FString OutString; + + FJsonObjectWrapper Wrapper; + Wrapper.JsonObject = MakeShared(); + FJsonObjectConverter::UStructToJsonObject(StaticStruct(), this, Wrapper.JsonObject.ToSharedRef(), 0, 0); + + if (!Wrapper.JsonObject.IsValid()) + { + IMTBL_ERR("Could not convert FImmutablePassportInitData to JSON") + return ""; + } + // Remove redirectUri field if it's empty so that the bridge doesn't try to + // use it + if (Wrapper.JsonObject->HasField("redirectUri") && Wrapper.JsonObject->GetStringField("redirectUri").IsEmpty()) + { + Wrapper.JsonObject->RemoveField("redirectUri"); + } + Wrapper.JsonObjectToString(OutString); + + return OutString; +} + +FString FImxBatchNftTransferRequest::ToJsonString() const +{ + FString OutString; + FJsonObjectWrapper Wrapper; + Wrapper.JsonObject = MakeShared(); + FJsonObjectConverter::UStructToJsonObject(StaticStruct(), this, Wrapper.JsonObject.ToSharedRef(), 0, 0); + + if (!Wrapper.JsonObject.IsValid()) + { + IMTBL_ERR("Could not convert FImxBatchNftTransferRequest to JSON") + return ""; + } + + if (Wrapper.JsonObject->HasField("nftTransferDetails")) + { + const auto Writer = TJsonWriterFactory>::Create(&OutString); + FJsonSerializer::Serialize(Wrapper.JsonObject->GetArrayField("nftTransferDetails"), Writer); + IMTBL_LOG("FImxBatchNftTransferRequest Serialised: %s", *OutString); + Writer->Close(); + } + return OutString; +} + +TOptional FImmutablePassportConnectData::FromJsonString(const FString& JsonObjectString) +{ + FImmutablePassportConnectData PassportConnect; + if (!FJsonObjectConverter::JsonObjectStringToUStruct(JsonObjectString, &PassportConnect, 0, 0)) + { + IMTBL_WARN("Could not parse response from JavaScript into the expected " "Passport connect format") + return TOptional(); + } + return PassportConnect; +} + +FString FImmutablePassportZkEvmRequestAccountsData::ToJsonString() const +{ + FString OutString; + FJsonObjectConverter::UStructToJsonObjectString(*this, OutString, 0, 0, 0, nullptr, false); + return OutString; +} + +TOptional FImmutablePassportZkEvmRequestAccountsData::FromJsonString( + const FString& JsonObjectString) +{ + FImmutablePassportZkEvmRequestAccountsData RequestAccounts; + if (!FJsonObjectConverter::JsonObjectStringToUStruct(JsonObjectString, &RequestAccounts, 0, 0)) + { + IMTBL_WARN("Could not parse response from JavaScript into the expected " + "Passport ZkEvm request accounts format") + return TOptional(); + } + return RequestAccounts; +} + +TOptional FImmutablePassportZkEvmRequestAccountsData::FromJsonObject( + const TSharedPtr& JsonObject) +{ + if (!JsonObject.IsValid()) { return TOptional(); } + + // Parse the JSON + FImmutablePassportZkEvmRequestAccountsData RequestAccounts; + if (!FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), &RequestAccounts, 0, 0)) + { + IMTBL_ERR("Could not parse response from JavaScript into the expected " + "Passport ZkEvm request accounts format") + return TOptional(); + } + return RequestAccounts; +} + +FString FImmutablePassportZkEvmGetBalanceData::ToJsonString() const +{ + FString OutString; + + FJsonObjectWrapper Wrapper; + Wrapper.JsonObject = MakeShared(); + FJsonObjectConverter::UStructToJsonObject(StaticStruct(), this, Wrapper.JsonObject.ToSharedRef(), 0, 0); + + if (!Wrapper.JsonObject.IsValid()) + { + IMTBL_ERR("Could not convert FImmutablePassportZkEvmGetBalanceData to JSON") + return ""; + } + Wrapper.JsonObjectToString(OutString); + + return OutString; } // @param Environment can be one of ImmutablePassportAction::EnvSandbox or // ImmutablePassportAction::EnvProduction -void UImmutablePassport::Initialize( - const FImmutablePassportInitData &Data, - const FImtblPassportResponseDelegate &ResponseDelegate) { - check(JSConnector.IsValid()); - - InitData = Data; +void UImmutablePassport::Initialize(const FImmutablePassportInitData& Data, + const FImtblPassportResponseDelegate& ResponseDelegate) +{ + check(JSConnector.IsValid()); - CallJS(ImmutablePassportAction::Initialize, InitData.ToJsonString(), - ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnInitializeResponse), - false); + InitData = Data; + + CallJS(ImmutablePassportAction::Initialize, InitData.ToJsonString(), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnInitializeResponse), false); } -void UImmutablePassport::Logout( - const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::Logout, TEXT(""), ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnLogoutResponse)); +void UImmutablePassport::Logout(const FImtblPassportResponseDelegate& ResponseDelegate) +{ +#if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC + if (bIsLoggedIn && IsPKCEConnected) + { + PKCELogoutResponseDelegate = ResponseDelegate; + } +#endif + CallJS(ImmutablePassportAction::Logout, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnLogoutResponse)); } -void UImmutablePassport::Connect( - const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::Connect, TEXT(""), ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnConnectResponse)); +void UImmutablePassport::Connect(const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::Connect, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnConnectResponse)); } -void UImmutablePassport::Reconnect(const FImtblPassportResponseDelegate& ResponseDelegate) +void UImmutablePassport::ConnectSilent(const FImtblPassportResponseDelegate& ResponseDelegate) { - CallJS(ImmutablePassportAction::Reconnect, TEXT(""), ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnReconnectResponse)); + CallJS(ImmutablePassportAction::ConnectSilent, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnConnectSilentResponse)); } -void UImmutablePassport::ConnectEvm( - const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::ConnectEvm, TEXT(""), ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnConnectEvmResponse)); +void UImmutablePassport::ConnectEvm(const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::ConnectEvm, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnConnectEvmResponse)); } -void UImmutablePassport::ZkEvmRequestAccounts( - const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::ZkEvmRequestAccounts, TEXT(""), - ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnZkEvmRequestAccountsResponse)); +void UImmutablePassport::ZkEvmRequestAccounts(const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::ZkEvmRequestAccounts, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnZkEvmRequestAccountsResponse)); } -void UImmutablePassport::ZkEvmGetBalance( - const FImmutablePassportZkEvmGetBalanceData &Data, - const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::ZkEvmGetBalance, Data.ToJsonString(), - ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnZkEvmGetBalanceResponse)); +void UImmutablePassport::ZkEvmGetBalance(const FImmutablePassportZkEvmGetBalanceData& Data, + const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::ZkEvmGetBalance, Data.ToJsonString(), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnZkEvmGetBalanceResponse)); } -void UImmutablePassport::ZkEvmSendTransaction( - const FImtblTransactionRequest &Request, - const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::ZkEvmSendTransaction, - UStructToJsonString(Request), ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnZkEvmSendTransactionResponse)); +void UImmutablePassport::ZkEvmSendTransaction(const FImtblTransactionRequest& Request, + const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::ZkEvmSendTransaction, UStructToJsonString(Request), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnZkEvmSendTransactionResponse)); } -void UImmutablePassport::ConfirmCode( - const FString &DeviceCode, const float Interval, - const FImtblPassportResponseDelegate &ResponseDelegate) { - FImmutablePassportCodeConfirmRequestData Data; - Data.deviceCode = DeviceCode; - Data.interval = Interval; - CallJS(ImmutablePassportAction::ConfirmCode, UStructToJsonString(Data), - ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnConfirmCodeResponse)); +void UImmutablePassport::ConfirmCode(const FString& DeviceCode, const float Interval, + const FImtblPassportResponseDelegate& ResponseDelegate) +{ + FImmutablePassportCodeConfirmRequestData Data; + + Data.deviceCode = DeviceCode; + Data.interval = Interval; + CallJS(ImmutablePassportAction::ConfirmCode, UStructToJsonString(Data), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnConfirmCodeResponse)); } #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC -void UImmutablePassport::ConnectPKCE( - const FImtblPassportResponseDelegate &ResponseDelegate) { +void UImmutablePassport::ConnectPKCE(const FImtblPassportResponseDelegate &ResponseDelegate) +{ #if PLATFORM_ANDROID - completingPKCE = false; + completingPKCE = false; #endif - PKCEResponseDelegate = ResponseDelegate; - CallJS(ImmutablePassportAction::GetPKCEAuthUrl, TEXT(""), - PKCEResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnGetPKCEAuthUrlResponse)); + PKCEResponseDelegate = ResponseDelegate; + CallJS(ImmutablePassportAction::GetPKCEAuthUrl, TEXT(""), PKCEResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnGetPKCEAuthUrlResponse)); } #endif -void UImmutablePassport::GetIdToken(const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::GetIdToken, TEXT(""), - ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnGetIdTokenResponse)); +void UImmutablePassport::GetIdToken(const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::GetIdToken, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnGetIdTokenResponse)); } -void UImmutablePassport::GetAccessToken(const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::GetAccessToken, TEXT(""), - ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnGetAccessTokenResponse)); +void UImmutablePassport::GetAccessToken(const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::GetAccessToken, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnGetAccessTokenResponse)); } -void UImmutablePassport::GetAddress( - const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::GetAddress, TEXT(""), ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnGetAddressResponse)); +void UImmutablePassport::GetAddress(const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::GetAddress, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnGetAddressResponse)); } -void UImmutablePassport::GetEmail( - const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::GetEmail, TEXT(""), ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnGetEmailResponse)); +void UImmutablePassport::GetEmail(const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::GetEmail, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnGetEmailResponse)); } -void UImmutablePassport::ImxTransfer( - const FImxTransferRequest &RequestData, - const FImtblPassportResponseDelegate &ResponseDelegate) { - IMTBL_LOG("Tranfer Request: %s", *UStructToJsonString(RequestData)) - CallJS(ImmutablePassportAction::ImxTransfer, UStructToJsonString(RequestData), - ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnTransferResponse)); +void UImmutablePassport::ImxTransfer(const FImxTransferRequest& RequestData, + const FImtblPassportResponseDelegate& ResponseDelegate) +{ + IMTBL_LOG("Tranfer Request: %s", *UStructToJsonString(RequestData)) + CallJS(ImmutablePassportAction::ImxTransfer, UStructToJsonString(RequestData), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnTransferResponse)); } -void UImmutablePassport::ImxBatchNftTransfer( - const FImxBatchNftTransferRequest &RequestData, - const FImtblPassportResponseDelegate &ResponseDelegate) { - CallJS(ImmutablePassportAction::ImxBatchNftTransfer, - RequestData.ToJsonString(), ResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnBatchNftTransferResponse)); +void UImmutablePassport::ImxBatchNftTransfer(const FImxBatchNftTransferRequest& RequestData, + const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::ImxBatchNftTransfer, RequestData.ToJsonString(), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnBatchNftTransferResponse)); } void UImmutablePassport::ImxIsRegisteredOffchain(const FImtblPassportResponseDelegate& ResponseDelegate) { - CallJS(ImmutablePassportAction::ImxIsRegisteredOffchain, TEXT(""), ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnImxIsRegisteredOffchain)); + CallJS(ImmutablePassportAction::ImxIsRegisteredOffchain, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnImxIsRegisteredOffchain)); } void UImmutablePassport::ImxRegisterOffchain(const FImtblPassportResponseDelegate& ResponseDelegate) { - CallJS(ImmutablePassportAction::ImxRegisterOffchain, TEXT(""), ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnImxRegisterOffchain)); + CallJS(ImmutablePassportAction::ImxRegisterOffchain, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnImxRegisterOffchain)); } void UImmutablePassport::HasStoredCredentials(const FImtblPassportResponseDelegate& ResponseDelegate) { // we do check credentials into two steps, we check accessToken and then IdToken // check access token - CallJS(ImmutablePassportAction::GetAccessToken, TEXT(""), ResponseDelegate, - FImtblJSResponseDelegate::CreateLambda([=](FImtblJSResponse Response) + CallJS(ImmutablePassportAction::GetAccessToken, TEXT(""), ResponseDelegate, FImtblJSResponseDelegate::CreateLambda( + [=](FImtblJSResponse Response) + { + FString AccessToken; + + Response.JsonObject->TryGetStringField(TEXT("result"), AccessToken); + if (!Response.success || AccessToken.IsEmpty()) + { + ResponseDelegate.ExecuteIfBound(FImmutablePassportResult{ + false, + Response.JsonObject->HasField(TEXT("error")) + ? Response.JsonObject->GetStringField(TEXT("error")) + : "Failed to retrieve Access Token.", + Response + }); + } + else + { + // check for id token + CallJS(ImmutablePassportAction::GetIdToken, TEXT(""), ResponseDelegate, + FImtblJSResponseDelegate::CreateLambda([ResponseDelegate](FImtblJSResponse Response) + { + FString IdToken; + + Response.JsonObject->TryGetStringField(TEXT("result"), IdToken); + if (!Response.success || IdToken.IsEmpty()) + { + ResponseDelegate.ExecuteIfBound(FImmutablePassportResult{ + false, + Response.JsonObject->HasField(TEXT("error")) + ? Response.JsonObject->GetStringField(TEXT("error")) + : "Failed to retrieve Id Token.", + Response + }); + } + else + { + ResponseDelegate.ExecuteIfBound(FImmutablePassportResult{ + Response.success, "", Response + }); + } + })); + } + })); +} + +void UImmutablePassport::Setup(const TWeakObjectPtr Connector) +{ + IMTBL_LOG_FUNCSIG + + if (!Connector.IsValid()) + { + IMTBL_ERR("Invalid JSConnector passed to UImmutablePassport::Initialize") + return; + } + + JSConnector = Connector.Get(); +} + +bool UImmutablePassport::CheckIsInitialized(const FString& Action, + const FImtblPassportResponseDelegate& ResponseDelegate) const +{ + if (!bIsInitialized) + { + IMTBL_WARN("Attempting action '%s' before Passport is initialized", *Action) + ResponseDelegate.ExecuteIfBound(FImmutablePassportResult{false, "Passport is not initialized"}); + } + return bIsInitialized; +} + +void UImmutablePassport::CallJS(const FString& Action, const FString& Data, + const FImtblPassportResponseDelegate& ClientResponseDelegate, + const FImtblJSResponseDelegate& HandleJSResponse, + const bool bCheckInitialized /*= true*/) +{ + if (bCheckInitialized && !CheckIsInitialized(Action, ClientResponseDelegate)) { return; } + + check(JSConnector.IsValid()); + const FString Guid = JSConnector->CallJS(Action, Data, HandleJSResponse); + ResponseDelegates.Add(Guid, ClientResponseDelegate); +} + +TOptional UImmutablePassport::GetResponseDelegate( + const FImtblJSResponse& Response) +{ + FImtblPassportResponseDelegate ResponseDelegate; + if (!ResponseDelegates.RemoveAndCopyValue(Response.requestId, ResponseDelegate)) + { + IMTBL_WARN("Couldn't find delegate for %s response", *Response.responseFor) + return TOptional(); + } + return ResponseDelegate; +} + +void UImmutablePassport::OnInitializeResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + FString Msg; + if (Response.success) { - FString AccessToken; - - Response.JsonObject->TryGetStringField(TEXT("result"), AccessToken); - if (!Response.success || AccessToken.IsEmpty()) - { - ResponseDelegate.ExecuteIfBound(FImmutablePassportResult{false, - Response.JsonObject->HasField(TEXT("error")) ? Response.JsonObject->GetStringField(TEXT("error")) : "Failed to retrieve Access Token.", - Response}); - } - else + bIsInitialized = true; + IMTBL_LOG("Passport initialization succeeded.") + } + else + { + IMTBL_ERR("Passport initialization failed.") + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + } + + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ Response.success, Msg, Response }); + } +} + +void UImmutablePassport::OnLogoutResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + if (Response.success) + { + FString Url; + FString Err; + + Response.JsonObject->TryGetStringField(TEXT("result"), Url); + if (!Url.IsEmpty()) { - // check for id token - CallJS(ImmutablePassportAction::GetIdToken, TEXT(""), ResponseDelegate, - FImtblJSResponseDelegate::CreateLambda([ResponseDelegate](FImtblJSResponse Response) +#if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC + if (IsPKCEConnected) { - FString IdToken; - - Response.JsonObject->TryGetStringField(TEXT("result"), IdToken); - if (!Response.success || IdToken.IsEmpty()) - { - ResponseDelegate.ExecuteIfBound(FImmutablePassportResult{false, - Response.JsonObject->HasField(TEXT("error")) ? Response.JsonObject->GetStringField(TEXT("error")) : "Failed to retrieve Id Token.", - Response}); - } - else + OnHandleDeepLink = FImtblPassportHandleDeepLinkDelegate::CreateUObject(this, &UImmutablePassport::OnDeepLinkActivated); +#if PLATFORM_ANDROID + LaunchAndroidUrl(Url); +#elif PLATFORM_IOS + [[ImmutableIOS instance] launchUrl:TCHAR_TO_ANSI(*Url)]; +#elif PLATFORM_MAC + [[ImmutableMac instance] launchUrl:TCHAR_TO_ANSI(*Url) forRedirectUri:TCHAR_TO_ANSI(*InitData.logoutRedirectUri)]; +#endif + } + else + { +#endif + FPlatformProcess::LaunchURL(*Url, nullptr, &Err); + if (Err.Len()) { - ResponseDelegate.ExecuteIfBound(FImmutablePassportResult{Response.success, "", Response}); + FString Msg = "Failed to connect to Browser: " + Err; + IMTBL_ERR("%s", *Msg); + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ false, Msg, Response }); + return; } - })); - } - })); -} - -void UImmutablePassport::Setup( - const TWeakObjectPtr Connector) { - IMTBL_LOG_FUNCSIG - - if (!Connector.IsValid()) { - IMTBL_ERR("Invalid JSConnector passed to UImmutablePassport::Initialize") - return; - } - - JSConnector = Connector.Get(); -} - -bool UImmutablePassport::CheckIsInitialized( - const FString &Action, - const FImtblPassportResponseDelegate &ResponseDelegate) const { - if (!bIsInitialized) { - IMTBL_WARN("Attempting action '%s' before Passport is initialized", *Action) - ResponseDelegate.ExecuteIfBound( - FImmutablePassportResult{false, "Passport is not initialized"}); - } - return bIsInitialized; -} - -void UImmutablePassport::CallJS( - const FString &Action, const FString &Data, - const FImtblPassportResponseDelegate &ClientResponseDelegate, - const FImtblJSResponseDelegate &HandleJSResponse, - const bool bCheckInitialized /*= true*/) { - if (bCheckInitialized && !CheckIsInitialized(Action, ClientResponseDelegate)) - return; - - check(JSConnector.IsValid()); - const FString Guid = JSConnector->CallJS(Action, Data, HandleJSResponse); - ResponseDelegates.Add(Guid, ClientResponseDelegate); -} - -TOptional -UImmutablePassport::GetResponseDelegate(const FImtblJSResponse &Response) { - FImtblPassportResponseDelegate ResponseDelegate; - if (!ResponseDelegates.RemoveAndCopyValue(Response.requestId, - ResponseDelegate)) { - IMTBL_WARN("Couldn't find delegate for %s response", *Response.responseFor) - return TOptional(); - } - return ResponseDelegate; -} - -void UImmutablePassport::OnInitializeResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - FString Msg; - if (Response.success) { - bIsInitialized = true; - IMTBL_LOG("Passport initialization succeeded.") - } else { - IMTBL_ERR("Passport initialization failed.") - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - } - - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{Response.success, Msg, Response}); - } -} - -void UImmutablePassport::OnLogoutResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - FString Msg; - if (Response.success) { - IMTBL_LOG("Logged out.") - bIsLoggedIn = false; - } else { - IMTBL_ERR("Error logging out.") - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - } - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{Response.success, Msg, Response}); - } -} - -void UImmutablePassport::OnConnectResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - const auto ConnectData = - JsonObjectToUStruct(Response.JsonObject); - if (!Response.success || !ConnectData || !ConnectData->code.Len()) { - FString Msg; - IMTBL_WARN("Connect attempt failed."); - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{false, Msg, Response}); - return; - } - FString Err; - FPlatformProcess::LaunchURL(*ConnectData->url, nullptr, &Err); - if (Err.Len()) { - FString Msg = "Failed to connect to Browser: " + Err; - IMTBL_ERR("%s", *Msg); - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{false, Msg, Response}); - return; - } - ConfirmCode(ConnectData->deviceCode, ConnectData->interval, - ResponseDelegate.GetValue()); - } -} - -void UImmutablePassport::OnReconnectResponse(FImtblJSResponse Response) -{ - if (auto ResponseDelegate = GetResponseDelegate(Response)) { + IMTBL_LOG("Logged out.") + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ Response.success, "Logged out" }); +#if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC + } +#endif + } + else + { + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ false, "Logout Url is empty", Response }); + } + bIsLoggedIn = false; + } + else + { + FString Msg = Response.Error.IsSet() ? Response.Error->ToString() : Response.JsonObject->GetStringField(TEXT("error")); + + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ Response.success, Msg, Response }); + + IMTBL_ERR("Error logging out.") + } + } +} + +void UImmutablePassport::OnConnectResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + const auto ConnectData = JsonObjectToUStruct(Response.JsonObject); + if (!Response.success || !ConnectData || !ConnectData->code.Len()) + { + FString Msg; + IMTBL_WARN("Connect attempt failed."); + Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error")); + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ false, Msg, Response }); + return; + } + FString Err; + FPlatformProcess::LaunchURL(*ConnectData->url, nullptr, &Err); + if (Err.Len()) + { + FString Msg = "Failed to connect to Browser: " + Err; + IMTBL_ERR("%s", *Msg); + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ false, Msg, Response }); + return; + } + ConfirmCode(ConnectData->deviceCode, ConnectData->interval, ResponseDelegate.GetValue()); + } +} + +void UImmutablePassport::OnConnectSilentResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { FString Msg; - + if (Response.success) { IMTBL_LOG("Reconnected.") @@ -448,491 +490,543 @@ void UImmutablePassport::OnReconnectResponse(FImtblJSResponse Response) else { IMTBL_ERR("Failed to reconnect.") - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); + Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error")); } - ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{Response.success, Msg, Response}); + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ Response.success, Msg, Response }); } } -void UImmutablePassport::OnConnectEvmResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - FString Msg; - if (Response.success) { - IMTBL_LOG("Connected to Evm.") - } else { - IMTBL_WARN("Error connecting to Evm.") - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - } - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{Response.success, Msg}); - } +void UImmutablePassport::OnConnectEvmResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + FString Msg; + if (Response.success) + { + IMTBL_LOG("Connected to Evm.") + } + else + { + IMTBL_WARN("Error connecting to Evm.") + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{Response.success, Msg}); + } } #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC -void UImmutablePassport::OnGetPKCEAuthUrlResponse(FImtblJSResponse Response) { - if (PKCEResponseDelegate.IsBound()) { - FString Msg; - bool bSuccess = true; - if (!Response.success || - !Response.JsonObject->HasTypedField(TEXT("result"))) { - IMTBL_LOG("Could not get PKCE auth URL from Passport."); - } else { - // Handle deeplink calls - OnHandleDeepLink = FImtblPassportHandleDeepLinkDelegate::CreateUObject( - this, &UImmutablePassport::OnDeepLinkActivated); - - FString Err; - Msg = Response.JsonObject->GetStringField(TEXT("result")) - .Replace(TEXT(" "), TEXT("+")); +void UImmutablePassport::OnGetPKCEAuthUrlResponse(FImtblJSResponse Response) +{ + if (PKCEResponseDelegate.IsBound()) + { + FString Msg; + bool bSuccess = true; + + if (!Response.success || !Response.JsonObject->HasTypedField(TEXT("result"))) + { + IMTBL_LOG("Could not get PKCE auth URL from Passport."); + } + else + { + // Handle deeplink calls + OnHandleDeepLink = FImtblPassportHandleDeepLinkDelegate::CreateUObject(this, &UImmutablePassport::OnDeepLinkActivated); + + Msg = Response.JsonObject->GetStringField(TEXT("result")).Replace(TEXT(" "), TEXT("+")); #if PLATFORM_ANDROID - OnPKCEDismissed = FImtblPassportOnPKCEDismissedDelegate::CreateUObject( - this, &UImmutablePassport::HandleOnPKCEDismissed); - - JNIEnv *Env = FAndroidApplication::GetJavaEnv(); - if (Env) { - jstring jurl = Env->NewStringUTF(TCHAR_TO_UTF8(*Msg)); - jclass jimmutableAndroidClass = FAndroidApplication::FindJavaClass( - "com/immutable/unreal/ImmutableAndroid"); - static jmethodID jlaunchUrl = FJavaWrapper::FindStaticMethod( - Env, jimmutableAndroidClass, "launchUrl", - "(Landroid/app/Activity;Ljava/lang/String;)V", false); - CallJniStaticVoidMethod(Env, jimmutableAndroidClass, jlaunchUrl, - FJavaWrapper::GameActivityThis, jurl); - } + OnPKCEDismissed = FImtblPassportOnPKCEDismissedDelegate::CreateUObject(this, &UImmutablePassport::HandleOnLoginPKCEDismissed); + LaunchAndroidUrl(Msg); #elif PLATFORM_IOS - [[ImmutableIOS instance] launchUrl:TCHAR_TO_ANSI(*Msg)]; + [[ImmutableIOS instance] launchUrl:TCHAR_TO_ANSI(*Msg)]; #elif PLATFORM_MAC - [[ImmutableMac instance] launchUrl:TCHAR_TO_ANSI(*Msg) - forRedirectUri:TCHAR_TO_ANSI(*InitData.redirectUri)]; + [[ImmutableMac instance] launchUrl:TCHAR_TO_ANSI(*Msg) forRedirectUri:TCHAR_TO_ANSI(*InitData.redirectUri)]; #endif + } + } + else + { + IMTBL_ERR("Unable to return a response for Connect PKCE"); + } +} - if (Err.Len()) { - Msg = "Failed to connect to Browser: " + Err; - IMTBL_ERR("%s", *Msg); - bSuccess = false; - } else { - return; - } - } - PKCEResponseDelegate.ExecuteIfBound( - FImmutablePassportResult{bSuccess, Msg}); - PKCEResponseDelegate = nullptr; - } else { - IMTBL_ERR("Unable to return a response for Connect PKCE"); - } -} - -void UImmutablePassport::OnConnectPKCEResponse(FImtblJSResponse Response) { - if (PKCEResponseDelegate.IsBound()) { - FString Msg; - if (Response.success) { - IMTBL_LOG("Successfully connected via PKCE") - bIsLoggedIn = true; - } else { - IMTBL_WARN("Connect PKCE attempt failed."); - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - } - PKCEResponseDelegate.ExecuteIfBound( - FImmutablePassportResult{Response.success, Msg}); - PKCEResponseDelegate = nullptr; - } else { - IMTBL_ERR("Unable to return a response for Connect PKCE"); - } +void UImmutablePassport::OnConnectPKCEResponse(FImtblJSResponse Response) +{ + if (PKCEResponseDelegate.IsBound()) + { + FString Msg; + + if (Response.success) + { + IMTBL_LOG("Successfully connected via PKCE") + bIsLoggedIn = true; + IsPKCEConnected = true; + } + else + { + IMTBL_WARN("Connect PKCE attempt failed."); + Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error")); + } + PKCEResponseDelegate.ExecuteIfBound(FImmutablePassportResult{Response.success, Msg}); + PKCEResponseDelegate = nullptr; + } + else + { + IMTBL_ERR("Unable to return a response for Connect PKCE"); + } #if PLATFORM_ANDROID - completingPKCE = false; + completingPKCE = false; #endif } #endif void UImmutablePassport::OnGetIdTokenResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) - { - FString IdToken; + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + FString IdToken; - Response.JsonObject->TryGetStringField(TEXT("result"), IdToken); + Response.JsonObject->TryGetStringField(TEXT("result"), IdToken); - if (!Response.success || IdToken.IsEmpty()) - { - IMTBL_LOG("Failed to retrieve Id Token."); + if (!Response.success || IdToken.IsEmpty()) + { + IMTBL_LOG("Failed to retrieve Id Token."); - const FString Msg = Response.JsonObject->HasField(TEXT("error")) ? Response.JsonObject->GetStringField(TEXT("error")) : "Failed to retrieve Id Token."; + const FString Msg = Response.JsonObject->HasField(TEXT("error")) + ? Response.JsonObject->GetStringField(TEXT("error")) + : "Failed to retrieve Id Token."; - ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ false, Msg, Response }); - } - else - { - IMTBL_LOG("Retrieved Id Token."); - ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ true, IdToken }); - } - } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{false, Msg, Response}); + } + else + { + IMTBL_LOG("Retrieved Id Token."); + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{true, IdToken}); + } + } } void UImmutablePassport::OnGetAccessTokenResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) - { - FString AccessToken; - - Response.JsonObject->TryGetStringField(TEXT("result"), AccessToken); - - if (!Response.success || AccessToken.IsEmpty()) - { - IMTBL_LOG("Failed to retrieve Access Token"); - - const FString Msg = Response.JsonObject->HasField(TEXT("error")) ? Response.JsonObject->GetStringField(TEXT("error")) : "Failed to retrieve Access Token."; - - ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ false, Msg, Response }); - } - else - { - IMTBL_LOG("Retrieved Access Token."); - ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ true, AccessToken }); - } - } -} - -void UImmutablePassport::OnGetAddressResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - auto ConnectData = - JsonObjectToUStruct(Response.JsonObject); - - FString Msg; - bool bSuccess = true; - if (!Response.success || - !Response.JsonObject->HasTypedField(TEXT("result"))) { - IMTBL_WARN("Could not fetch address from Passport."); - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - bSuccess = false; - } else { - Msg = Response.JsonObject->GetStringField(TEXT("result")); - } - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{bSuccess, Msg, Response}); - } -} - -void UImmutablePassport::OnZkEvmRequestAccountsResponse( - FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - const auto RequestAccountstData = - FImmutablePassportZkEvmRequestAccountsData::FromJsonObject( - Response.JsonObject); - FString Msg; - bool bSuccess = true; - if (!Response.success || - !Response.JsonObject->HasTypedField(TEXT("accounts"))) { - IMTBL_WARN("Error requesting zkevm accounts.") - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - bSuccess = false; - } else { - const auto size = RequestAccountstData->accounts.Num(); - for (int32 Index = 0; Index != size; ++Index) { - Msg += RequestAccountstData->accounts[Index]; - if (Index < size - 1) { - Msg += TEXT(","); - } - } - } - ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{bSuccess, Msg}); - } -} - -void UImmutablePassport::OnZkEvmGetBalanceResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - FString Msg; - bool bSuccess = true; - if (!Response.success || - !Response.JsonObject->HasTypedField(TEXT("result"))) { - IMTBL_WARN("Could not get balance."); - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - bSuccess = false; - } else { - Msg = Response.JsonObject->GetStringField(TEXT("result")); - } - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{bSuccess, Msg, Response}); - } -} - -void UImmutablePassport::OnZkEvmSendTransactionResponse( - FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - FString Msg; - bool bSuccess = true; - if (!Response.success || - !Response.JsonObject->HasTypedField(TEXT("result"))) { - IMTBL_WARN("Could not fetch send transaction."); - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - bSuccess = false; - } else { - Msg = Response.JsonObject->GetStringField(TEXT("result")); - } - ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{bSuccess, Msg}); - } -} - -void UImmutablePassport::OnConfirmCodeResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - FString Msg; - if (Response.success) { - IMTBL_LOG("Log in code confirmed.") - bIsLoggedIn = true; - } else { - IMTBL_WARN("Login code not confirmed.") - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - } - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{Response.success, Msg, Response}); - } -} - -void UImmutablePassport::OnGetEmailResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - auto ConnectData = - JsonObjectToUStruct(Response.JsonObject); - - FString Msg; - bool bSuccess = true; - if (!Response.success || - !Response.JsonObject->HasTypedField(TEXT("result"))) { - IMTBL_WARN("Connect attempt failed."); - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - bSuccess = false; - } else { - Msg = Response.JsonObject->GetStringField(TEXT("result")); - } - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{bSuccess, Msg, Response}); - } -} - -void UImmutablePassport::OnTransferResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - FString Msg; - bool bSuccess = true; - if (!Response.success) { - IMTBL_WARN("ImxTransfer failed."); - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - bSuccess = false; - } else { - Msg = Response.JsonObject->GetStringField(TEXT("status")); - } - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{bSuccess, Msg, Response}); - } -} - -void UImmutablePassport::OnBatchNftTransferResponse(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) { - auto BatchNftTransferResponse = - JsonObjectToUStruct(Response.JsonObject); - - FString Msg; - bool bSuccess = true; - if (!Response.success || - !Response.JsonObject->HasTypedField(TEXT("result"))) { - IMTBL_WARN("ImxBatchNftTransfer failed."); - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - bSuccess = false; - } else { - Msg = UStructToJsonString(*BatchNftTransferResponse); - } - ResponseDelegate->ExecuteIfBound( - FImmutablePassportResult{bSuccess, Msg, Response}); - } + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + FString AccessToken; + + Response.JsonObject->TryGetStringField(TEXT("result"), AccessToken); + + if (!Response.success || AccessToken.IsEmpty()) + { + IMTBL_LOG("Failed to retrieve Access Token"); + + const FString Msg = Response.JsonObject->HasField(TEXT("error")) + ? Response.JsonObject->GetStringField(TEXT("error")) + : "Failed to retrieve Access Token."; + + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{false, Msg, Response}); + } + else + { + IMTBL_LOG("Retrieved Access Token."); + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{true, AccessToken}); + } + } +} + +void UImmutablePassport::OnGetAddressResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + auto ConnectData = JsonObjectToUStruct(Response.JsonObject); + + FString Msg; + bool bSuccess = true; + if (!Response.success || !Response.JsonObject->HasTypedField(TEXT("result"))) + { + IMTBL_WARN("Could not fetch address from Passport."); + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + bSuccess = false; + } + else + { + Msg = Response.JsonObject->GetStringField(TEXT("result")); + } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{bSuccess, Msg, Response}); + } +} + +void UImmutablePassport::OnZkEvmRequestAccountsResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + const auto RequestAccountstData = FImmutablePassportZkEvmRequestAccountsData::FromJsonObject( + Response.JsonObject); + FString Msg; + bool bSuccess = true; + if (!Response.success || !Response.JsonObject->HasTypedField(TEXT("accounts"))) + { + IMTBL_WARN("Error requesting zkevm accounts.") + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + bSuccess = false; + } + else + { + const auto size = RequestAccountstData->accounts.Num(); + for (int32 Index = 0; Index != size; ++Index) + { + Msg += RequestAccountstData->accounts[Index]; + if (Index < size - 1) + { + Msg += TEXT(","); + } + } + } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{bSuccess, Msg}); + } +} + +void UImmutablePassport::OnZkEvmGetBalanceResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + FString Msg; + bool bSuccess = true; + if (!Response.success || !Response.JsonObject->HasTypedField(TEXT("result"))) + { + IMTBL_WARN("Could not get balance."); + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + bSuccess = false; + } + else + { + Msg = Response.JsonObject->GetStringField(TEXT("result")); + } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{bSuccess, Msg, Response}); + } +} + +void UImmutablePassport::OnZkEvmSendTransactionResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + FString Msg; + bool bSuccess = true; + if (!Response.success || !Response.JsonObject->HasTypedField(TEXT("result"))) + { + IMTBL_WARN("Could not fetch send transaction."); + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + bSuccess = false; + } + else + { + Msg = Response.JsonObject->GetStringField(TEXT("result")); + } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{bSuccess, Msg}); + } +} + +void UImmutablePassport::OnConfirmCodeResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + FString Msg; + + if (Response.success) + { + IMTBL_LOG("Log in code confirmed.") + bIsLoggedIn = true; + } + else + { + IMTBL_WARN("Login code not confirmed.") + Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error")); + } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ Response.success, Msg, Response }); + } +} + +void UImmutablePassport::OnGetEmailResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + auto ConnectData = JsonObjectToUStruct(Response.JsonObject); + + FString Msg; + bool bSuccess = true; + if (!Response.success || !Response.JsonObject->HasTypedField(TEXT("result"))) + { + IMTBL_WARN("Connect attempt failed."); + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + bSuccess = false; + } + else + { + Msg = Response.JsonObject->GetStringField(TEXT("result")); + } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{bSuccess, Msg, Response}); + } +} + +void UImmutablePassport::OnTransferResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + FString Msg; + bool bSuccess = true; + if (!Response.success) + { + IMTBL_WARN("ImxTransfer failed."); + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + bSuccess = false; + } + else + { + Msg = Response.JsonObject->GetStringField(TEXT("status")); + } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{bSuccess, Msg, Response}); + } +} + +void UImmutablePassport::OnBatchNftTransferResponse(FImtblJSResponse Response) +{ + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + auto BatchNftTransferResponse = JsonObjectToUStruct(Response.JsonObject); + + FString Msg; + bool bSuccess = true; + if (!Response.success || !Response.JsonObject->HasTypedField(TEXT("result"))) + { + IMTBL_WARN("ImxBatchNftTransfer failed."); + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + bSuccess = false; + } + else + { + Msg = UStructToJsonString(*BatchNftTransferResponse); + } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{bSuccess, Msg, Response}); + } } void UImmutablePassport::OnImxIsRegisteredOffchain(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) - { - FString Msg; - bool bResult = false; - - if (!Response.success) - { - IMTBL_WARN("ImxIsRegisteredOffchain failed."); - Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error")); - } - else - { - bResult = Response.JsonObject->GetBoolField(TEXT("result")); - } - ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ bResult, Msg, Response }); - } + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + FString Msg; + bool bResult = false; + + if (!Response.success) + { + IMTBL_WARN("ImxIsRegisteredOffchain failed."); + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + } + else + { + bResult = Response.JsonObject->GetBoolField(TEXT("result")); + } + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{bResult, Msg, Response}); + } } void UImmutablePassport::OnImxRegisterOffchain(FImtblJSResponse Response) { - if (auto ResponseDelegate = GetResponseDelegate(Response)) - { - FString Msg; - - if (!Response.success) - { - IMTBL_WARN("ImxRegisterOffchain failed."); - Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error")); - } - - ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ Response.success, Msg, Response }); - } -} - -void UImmutablePassport::LogAndIgnoreResponse(FImtblJSResponse Response) { - if (Response.success && !Response.Error) { - IMTBL_LOG("Received success response from Passport for action %s", - *Response.responseFor); - } else { - FString Msg; - Response.Error.IsSet() - ? Msg = Response.Error->ToString() - : Msg = Response.JsonObject->GetStringField(TEXT("error")); - IMTBL_WARN("Received error response from Passport for action %s -- %s", - *Response.responseFor, *Msg); - } + if (auto ResponseDelegate = GetResponseDelegate(Response)) + { + FString Msg; + + if (!Response.success) + { + IMTBL_WARN("ImxRegisterOffchain failed."); + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + } + + ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{Response.success, Msg, Response}); + } +} + +void UImmutablePassport::LogAndIgnoreResponse(FImtblJSResponse Response) +{ + if (Response.success && !Response.Error) + { + IMTBL_LOG("Received success response from Passport for action %s", *Response.responseFor); + } + else + { + FString Msg; + Response.Error.IsSet() + ? Msg = Response.Error->ToString() + : Msg = Response.JsonObject->GetStringField(TEXT("error")); + IMTBL_WARN("Received error response from Passport for action %s -- %s", *Response.responseFor, *Msg); + } } -#if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC -void UImmutablePassport::OnDeepLinkActivated(FString DeepLink) { - IMTBL_LOG("On Deep Link Activated: %s", *DeepLink); - OnHandleDeepLink = nullptr; - if (DeepLink.StartsWith(InitData.redirectUri)) { - CompletePKCEFlow(DeepLink); - } +#if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC +void UImmutablePassport::OnDeepLinkActivated(FString DeepLink) +{ + IMTBL_LOG_FUNC("URL : %s", *DeepLink); + OnHandleDeepLink = nullptr; + if (DeepLink.StartsWith(InitData.logoutRedirectUri)) + { + // execute on game thread + if (FTaskGraphInterface::IsRunning()) + { + FGraphEventRef GameThreadTask = FFunctionGraphTask::CreateAndDispatchWhenReady([this]() + { + PKCELogoutResponseDelegate.ExecuteIfBound(FImmutablePassportResult{ true, "Logged out" }); + PKCELogoutResponseDelegate = nullptr; + }, TStatId(), nullptr, ENamedThreads::GameThread); + } + + IsPKCEConnected = false; + } + else if (DeepLink.StartsWith(InitData.redirectUri)) + { + CompleteLoginPKCEFlow(DeepLink); + } } -void UImmutablePassport::CompletePKCEFlow(FString Url) { +void UImmutablePassport::CompleteLoginPKCEFlow(FString Url) +{ #if PLATFORM_ANDROID - completingPKCE = true; + completingPKCE = true; #endif - // Get code and state from deeplink URL - TOptional Code, State; - FString Endpoint, Params; - Url.Split(TEXT("?"), &Endpoint, &Params); - TArray ParamsArray; - Params.ParseIntoArray(ParamsArray, TEXT("&")); - for (FString Param : ParamsArray) { - FString Key, Value; - if (Param.StartsWith("code")) { - Param.Split(TEXT("="), &Key, &Value); - Code = Value; - } else if (Param.StartsWith("state")) { - Param.Split(TEXT("="), &Key, &Value); - State = Value; - } - } - - if (!Code.IsSet() || !State.IsSet()) { - const FString ErrorMsg = - "Uri was missing state and/or code. Please call ConnectPKCE() again"; - IMTBL_ERR("%s", *ErrorMsg); - PKCEResponseDelegate.ExecuteIfBound( - FImmutablePassportResult{false, ErrorMsg}); - PKCEResponseDelegate = nullptr; + // Get code and state from deeplink URL + TOptional Code, State; + FString Endpoint, Params; + Url.Split(TEXT("?"), &Endpoint, &Params); + TArray ParamsArray; + + Params.ParseIntoArray(ParamsArray, TEXT("&")); + for (FString Param : ParamsArray) + { + FString Key, Value; + + if (Param.StartsWith("code")) + { + Param.Split(TEXT("="), &Key, &Value); + Code = Value; + } + else if (Param.StartsWith("state")) + { + Param.Split(TEXT("="), &Key, &Value); + State = Value; + } + } + + if (!Code.IsSet() || !State.IsSet()) + { + const FString ErrorMsg = "Uri was missing state and/or code. Please call ConnectPKCE() again"; + + IMTBL_ERR("%s", *ErrorMsg); + PKCEResponseDelegate.ExecuteIfBound(FImmutablePassportResult{false, ErrorMsg}); + PKCEResponseDelegate = nullptr; #if PLATFORM_ANDROID - completingPKCE = false; + completingPKCE = false; #endif - } else { - FImmutablePassportConnectPKCEData Data = - FImmutablePassportConnectPKCEData{Code.GetValue(), State.GetValue()}; - CallJS(ImmutablePassportAction::ConnectPKCE, UStructToJsonString(Data), - PKCEResponseDelegate, - FImtblJSResponseDelegate::CreateUObject( - this, &UImmutablePassport::OnConnectPKCEResponse)); - } + } + else + { + FImmutablePassportConnectPKCEData Data = FImmutablePassportConnectPKCEData{Code.GetValue(), State.GetValue()}; + + CallJS(ImmutablePassportAction::ConnectPKCE, UStructToJsonString(Data), PKCEResponseDelegate, + FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnConnectPKCEResponse)); + } } #endif #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC #if PLATFORM_ANDROID // Called from Android JNI -void UImmutablePassport::HandleDeepLink(FString DeepLink) { +void UImmutablePassport::HandleDeepLink(FString DeepLink) const +{ #elif PLATFORM_IOS | PLATFORM_MAC // Called from iOS Objective C -void UImmutablePassport::HandleDeepLink(NSString *sDeepLink) { - FString DeepLink = FString(UTF8_TO_TCHAR([sDeepLink UTF8String])); - IMTBL_LOG("Handle Deep Link: %s", *DeepLink); +void UImmutablePassport::HandleDeepLink(NSString *sDeepLink) const +{ + FString DeepLink = FString(UTF8_TO_TCHAR([sDeepLink UTF8String])); + IMTBL_LOG("Handle Deep Link: %s", *DeepLink); #endif - if (!OnHandleDeepLink.ExecuteIfBound(DeepLink)) { - IMTBL_WARN("OnHandleDeepLink delegate was not called"); - } + if (!OnHandleDeepLink.ExecuteIfBound(DeepLink)) + { + IMTBL_WARN("OnHandleDeepLink delegate was not called"); + } } #endif #if PLATFORM_ANDROID -void UImmutablePassport::HandleOnPKCEDismissed() { - IMTBL_LOG("Handle On PKCE Dismissed"); - OnPKCEDismissed = nullptr; - if (!completingPKCE) { - // User hasn't entered all required details (e.g. email address) into - // Passport yet - IMTBL_LOG("PKCE dismissed before completing the flow"); - if (!PKCEResponseDelegate.ExecuteIfBound( - FImmutablePassportResult{false, "Cancelled"})) { - IMTBL_WARN("PKCEResponseDelegate delegate was not called"); - } - PKCEResponseDelegate = nullptr; - } else { - IMTBL_LOG("PKCE dismissed by user or SDK"); - } -} - -void UImmutablePassport::HandleCustomTabsDismissed() { - IMTBL_LOG("On PKCE Dismissed"); - if (!OnPKCEDismissed.ExecuteIfBound()) { - IMTBL_WARN("OnPKCEDismissed delegate was not called"); - } -} - -void UImmutablePassport::CallJniStaticVoidMethod(JNIEnv *Env, - const jclass Class, - jmethodID Method, ...) { - va_list Args; - va_start(Args, Method); - Env->CallStaticVoidMethodV(Class, Method, Args); - va_end(Args); - - Env->DeleteLocalRef(Class); +void UImmutablePassport::HandleOnLoginPKCEDismissed() +{ + IMTBL_LOG("Handle On Login PKCE Dismissed"); + OnPKCEDismissed = nullptr; + + if (!completingPKCE) + { + // User hasn't entered all required details (e.g. email address) into + // Passport yet + IMTBL_LOG("Login PKCE dismissed before completing the flow"); + if (!PKCEResponseDelegate.ExecuteIfBound(FImmutablePassportResult{ false, "Cancelled" })) + { + IMTBL_WARN("Login PKCEResponseDelegate delegate was not called"); + } + PKCEResponseDelegate = nullptr; + } + else + { + IMTBL_LOG("PKCE dismissed by user or SDK"); + } } -#endif -// void UImmutablePassport::OnReady(const FOnPassportReadyDelegate::FDelegate& -// Delegate) -// { -// if (JSConnector->IsBridgeReady()) -// { -// // TODO: run on next tick rather than immediately -// Delegate.ExecuteIfBound(); -// } -// else -// { -// OnPassportReady.Add(Delegate); -// } -// } \ No newline at end of file +void UImmutablePassport::HandleCustomTabsDismissed(FString Url) +{ + IMTBL_LOG("On PKCE Dismissed"); + + if (!OnPKCEDismissed.ExecuteIfBound()) + { + IMTBL_WARN("OnPKCEDismissed delegate was not called"); + } +} + +void UImmutablePassport::CallJniStaticVoidMethod(JNIEnv *Env, const jclass Class, jmethodID Method, ...) +{ + va_list Args; + + va_start(Args, Method); + Env->CallStaticVoidMethodV(Class, Method, Args); + va_end(Args); + Env->DeleteLocalRef(Class); +} + +void UImmutablePassport::LaunchAndroidUrl(FString Url) +{ + if (JNIEnv *Env = FAndroidApplication::GetJavaEnv()) + { + jstring jurl = Env->NewStringUTF(TCHAR_TO_UTF8(*Url)); + jclass jimmutableAndroidClass = FAndroidApplication::FindJavaClass("com/immutable/unreal/ImmutableAndroid"); + static jmethodID jlaunchUrl = FJavaWrapper::FindStaticMethod(Env, jimmutableAndroidClass, "launchUrl", "(Landroid/app/Activity;Ljava/lang/String;)V", false); + + CallJniStaticVoidMethod(Env, jimmutableAndroidClass, jlaunchUrl, FJavaWrapper::GameActivityThis, jurl); + } +} +#endif diff --git a/Source/Immutable/Private/Immutable/ImtblBlui.cpp b/Source/Immutable/Private/Immutable/ImtblBlui.cpp index 88a44fd5..ffe03772 100644 --- a/Source/Immutable/Private/Immutable/ImtblBlui.cpp +++ b/Source/Immutable/Private/Immutable/ImtblBlui.cpp @@ -141,6 +141,10 @@ void UImtblBlui::Init() { this, &UImtblBlui::WorldTickStart); } } + else + { + IMTBL_ERR("Failed to load Immutable bridge asset.") + } // Do this after the the page is given to the browser and being loaded... JSConnector->Init(!BluEye->IsBrowserLoading()); diff --git a/Source/Immutable/Private/Immutable/Mac/ImmutableMac.cpp b/Source/Immutable/Private/Immutable/Mac/ImmutableMac.cpp index 7c7efa00..411f7445 100644 --- a/Source/Immutable/Private/Immutable/Mac/ImmutableMac.cpp +++ b/Source/Immutable/Private/Immutable/Mac/ImmutableMac.cpp @@ -1,5 +1,11 @@ #include "ImmutableMac.h" #include "Immutable/ImmutablePassport.h" +#include "Immutable/ImmutableSubsystem.h" +#include "Engine/GameEngine.h" + +#if WITH_EDITOR +#include "Editor.h" +#endif ASWebAuthenticationSession *_authSession; @@ -19,6 +25,47 @@ ASWebAuthenticationSession *_authSession; return staticImmutableMac; } ++ (UImmutablePassport*) getPassport { + UWorld* World = nullptr; + +#if WITH_EDITOR + if (GEditor) + { + for (const auto& Context : GEditor->GetWorldContexts()) + { + if (Context.WorldType == EWorldType::PIE && Context.World()) + { + World = Context.World(); + break; + } + } + } +#else + if (UGameEngine* GameEngine = Cast(GEngine)) + { + World = GameEngine->GetGameWorld(); + } +#endif + + if (!World) { + return nil; + } + + auto ImmutableSubsystem = World->GetGameInstance()->GetSubsystem(); + + if (!ImmutableSubsystem) { + return nil; + } + + auto Passport = ImmutableSubsystem->GetPassport(); + + if (!Passport.IsValid()) { + return nil; + } + + return Passport.Get(); +} + - (void)launchUrl:(const char *)url forRedirectUri:(const char *)redirectUri { if (@available(macOS 10.15, *)) { NSURL *URL = @@ -40,7 +87,11 @@ ASWebAuthenticationSession *_authSession; _authSession = nil; if (callbackURL) { - UImmutablePassport::HandleDeepLink(callbackURL.absoluteString); + UImmutablePassport* passport = [ImmutableMac getPassport]; + + if (passport) { + passport->HandleDeepLink(callbackURL.absoluteString); + } } else { return; } diff --git a/Source/Immutable/Public/Immutable/Actions/ImtblBlueprintAsyncAction.h b/Source/Immutable/Public/Immutable/Actions/ImtblBlueprintAsyncAction.h index 465dbcef..03894205 100644 --- a/Source/Immutable/Public/Immutable/Actions/ImtblBlueprintAsyncAction.h +++ b/Source/Immutable/Public/Immutable/Actions/ImtblBlueprintAsyncAction.h @@ -4,21 +4,21 @@ #include "CoreMinimal.h" #include "Kismet/BlueprintAsyncActionBase.h" -// clang-format off + #include "ImtblBlueprintAsyncAction.generated.h" -// clang-format on /** - * + + * base class for asynchronous actions */ UCLASS() -class IMMUTABLE_API UImtblBlueprintAsyncAction - : public UBlueprintAsyncActionBase { - GENERATED_BODY() +class IMMUTABLE_API UImtblBlueprintAsyncAction : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() protected: - UObject *WorldContextObject; + UObject* WorldContextObject; - // Get subsystem - class UImmutableSubsystem *GetSubsystem() const; + // Get subsystem + class UImmutableSubsystem* GetSubsystem() const; }; diff --git a/Source/Immutable/Public/Immutable/Actions/ImtblPassportReconnectAsyncAction.h b/Source/Immutable/Public/Immutable/Actions/ImtblPassportConnectSilentAsyncAction.h similarity index 58% rename from Source/Immutable/Public/Immutable/Actions/ImtblPassportReconnectAsyncAction.h rename to Source/Immutable/Public/Immutable/Actions/ImtblPassportConnectSilentAsyncAction.h index 5659a56b..e6eaf0ac 100644 --- a/Source/Immutable/Public/Immutable/Actions/ImtblPassportReconnectAsyncAction.h +++ b/Source/Immutable/Public/Immutable/Actions/ImtblPassportConnectSilentAsyncAction.h @@ -3,13 +3,13 @@ #include "CoreMinimal.h" #include "ImtblBlueprintAsyncAction.h" -#include "ImtblPassportReconnectAsyncAction.generated.h" +#include "ImtblPassportConnectSilentAsyncAction.generated.h" /** * */ UCLASS() -class IMMUTABLE_API UImtblPassportReconnectAsyncAction : public UImtblBlueprintAsyncAction +class IMMUTABLE_API UImtblPassportConnectSilentAsyncAction : public UImtblBlueprintAsyncAction { GENERATED_BODY() @@ -18,7 +18,7 @@ class IMMUTABLE_API UImtblPassportReconnectAsyncAction : public UImtblBlueprintA public: UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", BlueprintInternalUseOnly = "true"), Category = "Immutable") - static UImtblPassportReconnectAsyncAction* Reconnect(UObject* WorldContextObject); + static UImtblPassportConnectSilentAsyncAction* ConnectSilent(UObject* WorldContextObject); void Activate() override; @@ -29,6 +29,6 @@ class IMMUTABLE_API UImtblPassportReconnectAsyncAction : public UImtblBlueprintA UPROPERTY(BlueprintAssignable) FPassportConnectSilentOutputPin OnFailure; - void DoReconnect(TWeakObjectPtr JSGetConnector); - void OnReconnectResponse(struct FImmutablePassportResult Result); + void DoConnectSilent(TWeakObjectPtr JSGetConnector); + void OnConnectSilentResponse(struct FImmutablePassportResult Result); }; diff --git a/Source/Immutable/Public/Immutable/Actions/ImtblPassportInitializationAsyncAction.h b/Source/Immutable/Public/Immutable/Actions/ImtblPassportInitializationAsyncAction.h index 8227dc98..b0c9ad85 100644 --- a/Source/Immutable/Public/Immutable/Actions/ImtblPassportInitializationAsyncAction.h +++ b/Source/Immutable/Public/Immutable/Actions/ImtblPassportInitializationAsyncAction.h @@ -5,40 +5,36 @@ #include "CoreMinimal.h" #include "Immutable/ImmutablePassport.h" #include "ImtblBlueprintAsyncAction.h" + #include "ImtblPassportInitializationAsyncAction.generated.h" /** - * + * Async action to instantiate */ UCLASS() -class IMMUTABLE_API UImtblPassportInitializationAsyncAction - : public UImtblBlueprintAsyncAction { - GENERATED_BODY() +class IMMUTABLE_API UImtblPassportInitializationAsyncAction : public UImtblBlueprintAsyncAction +{ + GENERATED_BODY() - DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPassportInitializationOutputPin, - FString, Message); + DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPassportInitializationOutputPin, FString, Message); public: - UFUNCTION(BlueprintCallable, - meta = (WorldContext = "WorldContextObject", - BlueprintInternalUseOnly = "true"), - Category = "Immutable") - static UImtblPassportInitializationAsyncAction * - InitializePassport(UObject *WorldContextObject, const FString &ClientID, - const FString &RedirectUri, const FString &Environment); + UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", BlueprintInternalUseOnly = "true"), Category = "Immutable") + static UImtblPassportInitializationAsyncAction* InitializePassport(UObject* WorldContextObject, const FString& ClientID, const FString& RedirectUri, const FString& LogoutUri, const FString& Environment); - void Activate() override; + virtual void Activate() override; private: - FString ClientId; - FString RedirectUri; - FString Environment; - - UPROPERTY(BlueprintAssignable) - FPassportInitializationOutputPin Initialized; - UPROPERTY(BlueprintAssignable) - FPassportInitializationOutputPin Failed; - - void DoInit(TWeakObjectPtr JSConnector); - void OnInitialized(FImmutablePassportResult Result); + FString ClientId; + FString RedirectUri; + FString LogoutUri; + FString Environment; + + UPROPERTY(BlueprintAssignable) + FPassportInitializationOutputPin Initialized; + UPROPERTY(BlueprintAssignable) + FPassportInitializationOutputPin Failed; + + void DoInit(TWeakObjectPtr JSConnector); + void OnInitialized(FImmutablePassportResult Result); }; diff --git a/Source/Immutable/Public/Immutable/Actions/ImtblPassportLogoutAsyncAction.h b/Source/Immutable/Public/Immutable/Actions/ImtblPassportLogoutAsyncAction.h index 88da5866..fe24f7b6 100644 --- a/Source/Immutable/Public/Immutable/Actions/ImtblPassportLogoutAsyncAction.h +++ b/Source/Immutable/Public/Immutable/Actions/ImtblPassportLogoutAsyncAction.h @@ -1,6 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once +#pragma once #include "CoreMinimal.h" #include "Immutable/ImmutablePassport.h" @@ -12,28 +10,29 @@ */ // UCLASS(meta = (HasDedicatedAsyncNode)) UCLASS() -class IMMUTABLE_API UImtblPassportLogoutAsyncAction - : public UImtblBlueprintAsyncAction { - GENERATED_BODY() +class IMMUTABLE_API UImtblPassportLogoutAsyncAction : public UImtblBlueprintAsyncAction +{ + GENERATED_BODY() - DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPassportLogoutOutputPin, FString, - ErrorMessage); + DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPassportLogoutOutPin, FString, Message); public: - UFUNCTION(BlueprintCallable, - meta = (WorldContext = "WorldContextObject", - BlueprintInternalUseOnly = "true"), - Category = "Immutable") - static UImtblPassportLogoutAsyncAction *Logout(UObject *WorldContextObject); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", BlueprintInternalUseOnly = "true"), Category = "Immutable") + static UImtblPassportLogoutAsyncAction* Logout(UObject* WorldContextObject); + + virtual void Activate() override; + +private: - void Activate() override; + void DoLogout(TWeakObjectPtr JSConnector); + void OnLogoutResponse(FImmutablePassportResult Result) const; private: - void DoLogout(TWeakObjectPtr JSConnector); - void OnLogoutResponse(FImmutablePassportResult Result); - UPROPERTY(BlueprintAssignable) - FPassportLogoutOutputPin LoggedOut; - UPROPERTY(BlueprintAssignable) - FPassportLogoutOutputPin Failed; + UPROPERTY(BlueprintAssignable) + FPassportLogoutOutPin OnSuccess; + UPROPERTY(BlueprintAssignable) + FPassportLogoutOutPin OnFailure; + }; diff --git a/Source/Immutable/Public/Immutable/ImmutablePassport.h b/Source/Immutable/Public/Immutable/ImmutablePassport.h index fa4befc2..d7101cd6 100644 --- a/Source/Immutable/Public/Immutable/ImmutablePassport.h +++ b/Source/Immutable/Public/Immutable/ImmutablePassport.h @@ -8,218 +8,226 @@ #include "Misc/EngineVersion.h" #include "Runtime/Core/Public/HAL/Platform.h" #include "UObject/Object.h" -// clang-format off + #include "ImmutablePassport.generated.h" -// clang-format on + struct FImtblJSResponse; -namespace ImmutablePassportAction { -const FString Initialize = TEXT("init"); -const FString Logout = TEXT("logout"); -const FString Connect = TEXT("connect"); -const FString Reconnect = TEXT("reconnect"); -const FString ConnectEvm = TEXT("connectEvm"); -const FString ZkEvmRequestAccounts = TEXT("zkEvmRequestAccounts"); -const FString ZkEvmGetBalance = TEXT("zkEvmGetBalance"); -const FString ZkEvmSendTransaction = TEXT("zkEvmSendTransaction"); -const FString ConfirmCode = TEXT("confirmCode"); +namespace ImmutablePassportAction +{ + const FString Initialize = TEXT("init"); + const FString Logout = TEXT("logout"); + const FString Connect = TEXT("connect"); + const FString ConnectSilent = TEXT("reconnect"); + const FString ConnectEvm = TEXT("connectEvm"); + const FString ZkEvmRequestAccounts = TEXT("zkEvmRequestAccounts"); + const FString ZkEvmGetBalance = TEXT("zkEvmGetBalance"); + const FString ZkEvmSendTransaction = TEXT("zkEvmSendTransaction"); + const FString ConfirmCode = TEXT("confirmCode"); #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC -const FString GetPKCEAuthUrl = TEXT("getPKCEAuthUrl"); -const FString ConnectPKCE = TEXT("connectPKCE"); + const FString GetPKCEAuthUrl = TEXT("getPKCEAuthUrl"); + const FString ConnectPKCE = TEXT("connectPKCE"); #endif -const FString GetAddress = TEXT("getAddress"); -const FString GetEmail = TEXT("getEmail"); -const FString GetAccessToken = TEXT("getAccessToken"); -const FString GetIdToken = TEXT("getIdToken"); -const FString ImxTransfer = TEXT("imxTransfer"); -const FString ImxBatchNftTransfer = TEXT("imxBatchNftTransfer"); -const FString EnvSandbox = TEXT("sandbox"); -const FString EnvProduction = TEXT("production"); -const FString ImxIsRegisteredOffchain = TEXT("isRegisteredOffchain"); -const FString ImxRegisterOffchain = TEXT("registerOffchain"); + const FString GetAddress = TEXT("getAddress"); + const FString GetEmail = TEXT("getEmail"); + const FString GetAccessToken = TEXT("getAccessToken"); + const FString GetIdToken = TEXT("getIdToken"); + const FString ImxTransfer = TEXT("imxTransfer"); + const FString ImxBatchNftTransfer = TEXT("imxBatchNftTransfer"); + const FString EnvSandbox = TEXT("sandbox"); + const FString EnvProduction = TEXT("production"); + const FString ImxIsRegisteredOffchain = TEXT("isRegisteredOffchain"); + const FString ImxRegisterOffchain = TEXT("registerOffchain"); } // namespace ImmutablePassportAction -template -FString UStructToJsonString(const UStructType &InStruct) { - FString OutString; - FJsonObjectConverter::UStructToJsonObjectString(InStruct, OutString, 0, 0, 0, - nullptr, false); - return OutString; +template FString UStructToJsonString(const UStructType& InStruct) +{ + FString OutString; + FJsonObjectConverter::UStructToJsonObjectString(InStruct, OutString, 0, 0, 0, nullptr, false); + return OutString; } -template -TOptional -JsonObjectToUStruct(const TSharedPtr &JsonObject) { - if (!JsonObject.IsValid()) { - return TOptional(); - } - - // Parse the JSON - UStructType StructInstance; - if (!FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), - &StructInstance, 0, 0)) { - IMTBL_ERR( - "Could not parse response from JavaScript into the expected Ustruct") - return TOptional(); - } - return StructInstance; +template TOptional JsonObjectToUStruct(const TSharedPtr& JsonObject) +{ + if (!JsonObject.IsValid()) + { + return TOptional(); + } + + // Parse the JSON + UStructType StructInstance; + if (!FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), &StructInstance, 0, 0)) + { + IMTBL_ERR("Could not parse response from JavaScript into the expected Ustruct") + return TOptional(); + } + return StructInstance; } USTRUCT() -struct FImmutablePassportResult { - GENERATED_BODY() +struct FImmutablePassportResult +{ + GENERATED_BODY() - UPROPERTY() - bool Success = false; - UPROPERTY() - FString Message; + UPROPERTY() + bool Success = false; + UPROPERTY() + FString Message; - FImtblJSResponse Response; + FImtblJSResponse Response; }; USTRUCT() -struct FImmutableEngineVersionData { - GENERATED_BODY() - - UPROPERTY() - FString engine = TEXT("unreal"); - - // cannot have spaces - UPROPERTY() - FString engineVersion = - FEngineVersion::Current().ToString().Replace(TEXT(" "), TEXT("_")); - - // cannot have spaces - UPROPERTY() - FString platform = FString(FPlatformProperties::IniPlatformName()) - .Replace(TEXT(" "), TEXT("_")); - - // cannot have spaces - UPROPERTY() - FString platformVersion = - FPlatformMisc::GetOSVersion().Replace(TEXT(" "), TEXT("_")); +struct FImmutableEngineVersionData +{ + GENERATED_BODY() + + UPROPERTY() + FString engine = TEXT("unreal"); + + // cannot have spaces + UPROPERTY() + FString engineVersion = FEngineVersion::Current().ToString().Replace(TEXT(" "), TEXT("_")); + + // cannot have spaces + UPROPERTY() + FString platform = FString(FPlatformProperties::IniPlatformName()).Replace(TEXT(" "), TEXT("_")); + + // cannot have spaces + UPROPERTY() + FString platformVersion = FPlatformMisc::GetOSVersion().Replace(TEXT(" "), TEXT("_")); }; USTRUCT() -struct FImmutablePassportInitData { - GENERATED_BODY() +struct FImmutablePassportInitData +{ + GENERATED_BODY() - UPROPERTY() - FString clientId; + UPROPERTY() + FString clientId; - UPROPERTY() - FString redirectUri; + UPROPERTY() + FString redirectUri; - UPROPERTY() - FString environment = ImmutablePassportAction::EnvSandbox; + UPROPERTY() + FString logoutRedirectUri; - UPROPERTY() - FImmutableEngineVersionData engineVersion; + UPROPERTY() + FString environment = ImmutablePassportAction::EnvSandbox; - FString ToJsonString() const; + UPROPERTY() + FImmutableEngineVersionData engineVersion; + + FString ToJsonString() const; }; USTRUCT() -struct FImmutablePassportConnectData { - GENERATED_BODY() - - UPROPERTY() - FString code; - UPROPERTY() - FString deviceCode; - UPROPERTY() - FString url; - UPROPERTY() - float interval = 0; - - static TOptional - FromJsonString(const FString &JsonObjectString); +struct FImmutablePassportConnectData +{ + GENERATED_BODY() + + UPROPERTY() + FString code; + UPROPERTY() + FString deviceCode; + UPROPERTY() + FString url; + UPROPERTY() + float interval = 0; + + static TOptional FromJsonString(const FString& JsonObjectString); }; USTRUCT() -struct FImmutablePassportZkEvmRequestAccountsData { - GENERATED_BODY() +struct FImmutablePassportZkEvmRequestAccountsData +{ + GENERATED_BODY() - UPROPERTY() - TArray accounts; + UPROPERTY() + TArray accounts; - FString ToJsonString() const; - static TOptional - FromJsonString(const FString &JsonObjectString); - static TOptional - FromJsonObject(const TSharedPtr &JsonObject); + FString ToJsonString() const; + static TOptional FromJsonString(const FString& JsonObjectString); + static TOptional FromJsonObject( + const TSharedPtr& JsonObject); }; USTRUCT() -struct FImmutablePassportZkEvmGetBalanceData { - GENERATED_BODY() +struct FImmutablePassportZkEvmGetBalanceData +{ + GENERATED_BODY() - UPROPERTY() - FString address; + UPROPERTY() + FString address; - UPROPERTY() - FString blockNumberOrTag = "latest"; + UPROPERTY() + FString blockNumberOrTag = "latest"; - FString ToJsonString() const; + FString ToJsonString() const; }; USTRUCT(BlueprintType) -struct FImtblAccessListItem { - GENERATED_BODY() +struct FImtblAccessListItem +{ + GENERATED_BODY() - UPROPERTY(BlueprintReadWrite, EditAnywhere) - FString address; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString address; - UPROPERTY(BlueprintReadWrite, EditAnywhere) - TArray storageKeys; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + TArray storageKeys; }; /** * Key Value wrappers for converting to JSON */ USTRUCT() -struct FStringCustomData { - GENERATED_BODY() +struct FStringCustomData +{ + GENERATED_BODY() - UPROPERTY() - FString key; + UPROPERTY() + FString key; - UPROPERTY() - FString value; + UPROPERTY() + FString value; }; USTRUCT() -struct FInt64CustomData { - GENERATED_BODY() +struct FInt64CustomData +{ + GENERATED_BODY() - UPROPERTY() - FString key; + UPROPERTY() + FString key; - UPROPERTY() - int64 value; + UPROPERTY() + int64 value; }; USTRUCT() -struct FFloatCustomData { - GENERATED_BODY() +struct FFloatCustomData +{ + GENERATED_BODY() - UPROPERTY() - FString key; + UPROPERTY() + FString key; - UPROPERTY() - float value; + UPROPERTY() + float value; }; USTRUCT() -struct FBoolCustomData { - GENERATED_BODY() +struct FBoolCustomData +{ + GENERATED_BODY() - UPROPERTY() - FString key; + UPROPERTY() + FString key; - UPROPERTY() - bool value; + UPROPERTY() + bool value; }; UENUM(BlueprintType) @@ -231,364 +239,359 @@ enum EImtblCustomDataType { String, Int64, Float, Bool }; * corresponding value. This will later be mapped to the proper API structure. */ USTRUCT(BlueprintType) -struct FImtblCustomData { - GENERATED_BODY() - - UPROPERTY(BlueprintReadWrite, EditAnywhere) - FString key; - - UPROPERTY(BlueprintReadWrite, EditAnywhere) - FString stringValue; - - UPROPERTY(BlueprintReadWrite, EditAnywhere) - int64 intValue; - - UPROPERTY(BlueprintReadWrite, EditAnywhere) - float floatValue; - - UPROPERTY(BlueprintReadWrite, EditAnywhere) - bool boolValue; - - UPROPERTY(BlueprintReadWrite, EditAnywhere) - TEnumAsByte type; - - /** - * Convert from blueprint structure to the expected API data structure - */ - TSharedPtr ToJsonObject() const { - switch (type) { - case String: - return FJsonObjectConverter::UStructToJsonObject( - {key, stringValue}); - case Int64: - return FJsonObjectConverter::UStructToJsonObject( - {key, intValue}); - case Float: - return FJsonObjectConverter::UStructToJsonObject( - {key, floatValue}); - case Bool: - return FJsonObjectConverter::UStructToJsonObject( - {key, boolValue}); - default: { - } - } - return {}; - } +struct FImtblCustomData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString key; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString stringValue; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int64 intValue; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + float floatValue; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool boolValue; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + TEnumAsByte type; + + /** + * Convert from blueprint structure to the expected API data structure + */ + TSharedPtr ToJsonObject() const + { + switch (type) + { + case String: return FJsonObjectConverter::UStructToJsonObject({key, stringValue}); + case Int64: return FJsonObjectConverter::UStructToJsonObject({key, intValue}); + case Float: return FJsonObjectConverter::UStructToJsonObject({key, floatValue}); + case Bool: return FJsonObjectConverter::UStructToJsonObject({key, boolValue}); + default: + { + } + } + return {}; + } }; USTRUCT(BlueprintType) -struct FImtblTransactionRequest { - GENERATED_BODY() +struct FImtblTransactionRequest +{ + GENERATED_BODY() - UPROPERTY(BlueprintReadWrite, EditAnywhere) - FString to; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString to; - UPROPERTY(BlueprintReadWrite, EditAnywhere) - FString data = "0x"; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString data = "0x"; - UPROPERTY(BlueprintReadWrite, EditAnywhere) - FString value; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString value; }; USTRUCT() -struct FImmutablePassportCodeConfirmRequestData { - GENERATED_BODY() - - UPROPERTY() - FString deviceCode; - UPROPERTY() - float interval = 5; - UPROPERTY() - float timeoutMs = 15 * 60 * 1000; +struct FImmutablePassportCodeConfirmRequestData +{ + GENERATED_BODY() + + UPROPERTY() + FString deviceCode; + UPROPERTY() + float interval = 5; + UPROPERTY() + float timeoutMs = 15 * 60 * 1000; }; USTRUCT() -struct FImxTransferRequest { - GENERATED_BODY() +struct FImxTransferRequest +{ + GENERATED_BODY() - UPROPERTY() - FString receiver; + UPROPERTY() + FString receiver; - UPROPERTY() - FString type; + UPROPERTY() + FString type; - UPROPERTY() - FString amount; + UPROPERTY() + FString amount; - UPROPERTY() - FString tokenId; + UPROPERTY() + FString tokenId; - UPROPERTY() - FString tokenAddress; + UPROPERTY() + FString tokenAddress; - FString ToJsonString() const; + FString ToJsonString() const; }; USTRUCT() -struct FImxTransferResponse { - GENERATED_BODY() +struct FImxTransferResponse +{ + GENERATED_BODY() - UPROPERTY() - FString sentSignature; + UPROPERTY() + FString sentSignature; - UPROPERTY() - FString status; + UPROPERTY() + FString status; - UPROPERTY() - float time; + UPROPERTY() + float time; - UPROPERTY() - unsigned transferId; + UPROPERTY() + unsigned transferId; }; USTRUCT(BlueprintType) -struct FNftTransferDetails { - GENERATED_BODY() +struct FNftTransferDetails +{ + GENERATED_BODY() - UPROPERTY(BlueprintReadWrite) - FString receiver; + UPROPERTY(BlueprintReadWrite) + FString receiver; - UPROPERTY(BlueprintReadWrite) - FString tokenId; + UPROPERTY(BlueprintReadWrite) + FString tokenId; - UPROPERTY(BlueprintReadWrite) - FString tokenAddress; + UPROPERTY(BlueprintReadWrite) + FString tokenAddress; }; USTRUCT() -struct FImxBatchNftTransferRequest { - GENERATED_BODY() +struct FImxBatchNftTransferRequest +{ + GENERATED_BODY() - UPROPERTY() - TArray nftTransferDetails; + UPROPERTY() + TArray nftTransferDetails; - FString ToJsonString() const; + FString ToJsonString() const; }; USTRUCT() -struct FImxBatchNftTransferResponse { - GENERATED_BODY() +struct FImxBatchNftTransferResponse +{ + GENERATED_BODY() - UPROPERTY() - TArray transferIds; + UPROPERTY() + TArray transferIds; }; USTRUCT() -struct FImmutablePassportConnectPKCEData { - GENERATED_BODY() +struct FImmutablePassportConnectPKCEData +{ + GENERATED_BODY() - UPROPERTY() - FString authorizationCode; + UPROPERTY() + FString authorizationCode; - UPROPERTY() - FString state; + UPROPERTY() + FString state; }; USTRUCT() -struct FImxRegisterOffchainResponse { - GENERATED_BODY() +struct FImxRegisterOffchainResponse +{ + GENERATED_BODY() - UPROPERTY() - FString tx_hash; + UPROPERTY() + FString tx_hash; }; -#if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC -DECLARE_DELEGATE_OneParam(FImtblPassportHandleDeepLinkDelegate, FString); -FImtblPassportHandleDeepLinkDelegate OnHandleDeepLink; -#endif -#if PLATFORM_ANDROID -DECLARE_DELEGATE(FImtblPassportOnPKCEDismissedDelegate); -FImtblPassportOnPKCEDismissedDelegate OnPKCEDismissed; -#endif /** * */ UCLASS() -class IMMUTABLE_API UImmutablePassport : public UObject { - GENERATED_BODY() - friend class UImmutableSubsystem; +class IMMUTABLE_API UImmutablePassport : public UObject +{ + GENERATED_BODY() + friend class UImmutableSubsystem; public: - DECLARE_MULTICAST_DELEGATE(FOnPassportReadyDelegate); + DECLARE_MULTICAST_DELEGATE(FOnPassportReadyDelegate); - DECLARE_DELEGATE_OneParam(FImtblPassportResponseDelegate, - FImmutablePassportResult); + DECLARE_DELEGATE_OneParam(FImtblPassportResponseDelegate, FImmutablePassportResult); #if PLATFORM_ANDROID - static void HandleDeepLink(FString DeepLink); - static void HandleCustomTabsDismissed(); + void HandleDeepLink(FString DeepLink) const; + void HandleCustomTabsDismissed(FString Url); #elif PLATFORM_IOS | PLATFORM_MAC - static void HandleDeepLink(NSString *sDeepLink); + void HandleDeepLink(NSString* sDeepLink) const; #endif - void Initialize(const FImmutablePassportInitData &InitData, - const FImtblPassportResponseDelegate &ResponseDelegate); - void Logout(const FImtblPassportResponseDelegate &ResponseDelegate); - void Connect(const FImtblPassportResponseDelegate &ResponseDelegate); - void Reconnect(const FImtblPassportResponseDelegate &ResponseDelegate); + void Initialize(const FImmutablePassportInitData& InitData, const FImtblPassportResponseDelegate& ResponseDelegate); + void Logout(const FImtblPassportResponseDelegate& ResponseDelegate); + void Connect(const FImtblPassportResponseDelegate& ResponseDelegate); + void ConnectSilent(const FImtblPassportResponseDelegate& ResponseDelegate); #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC - void ConnectPKCE(const FImtblPassportResponseDelegate &ResponseDelegate); + void ConnectPKCE(const FImtblPassportResponseDelegate& ResponseDelegate); #endif - /** - * Initializes the zkEVM provider. - * @param ResponseDelegate The response delegate of type - * FImtblPassportResponseDelegate to call on response from JS. - */ - void ConnectEvm(const FImtblPassportResponseDelegate &ResponseDelegate); - - /** - * This method attempts to authenticate the user and initialises their - * Passport wallet before returning an array of wallet addresses. - * - * This must be called before using other zkEVM functions. - * @param ResponseDelegate The response delegate of type - * FImtblPassportResponseDelegate to call on response from JS. - */ - void - ZkEvmRequestAccounts(const FImtblPassportResponseDelegate &ResponseDelegate); - - /** - * Returns the balance of the account of given address in wei. - * @param Data The address and block number for the request - * @param ResponseDelegate The response delegate of type - * FImtblPassportResponseDelegate to call on response from JS. - */ - void ZkEvmGetBalance(const FImmutablePassportZkEvmGetBalanceData &Data, - const FImtblPassportResponseDelegate &ResponseDelegate); - - /** - * Creates new message call transaction or a contract creation, if the data - * field contains code. - * @param Request The request data to perform the transaction. - * @param ResponseDelegate The response delegate of type - * FImtblPassportResponseDelegate to call on response from JS. - */ - void - ZkEvmSendTransaction(const FImtblTransactionRequest &Request, - const FImtblPassportResponseDelegate &ResponseDelegate); - void GetIdToken(const FImtblPassportResponseDelegate &ResponseDelegate); - void GetAccessToken(const FImtblPassportResponseDelegate &ResponseDelegate); - void GetAddress(const FImtblPassportResponseDelegate &ResponseDelegate); - void GetEmail(const FImtblPassportResponseDelegate &ResponseDelegate); - - /** - * Create a new imx transfer request. - * @param RequestData The transfer details structure of type - * FImxTransferRequest - * @param ResponseDelegate The response delegate of type - * FImtblPassportResponseDelegate to call on response from JS. - */ - void ImxTransfer(const FImxTransferRequest &RequestData, - const FImtblPassportResponseDelegate &ResponseDelegate); - - /** - * Creates a new imx batch nft transfer request with the given transfer - * details. - * @param RequestData The transfer details structure of type - * FImxBatchNftTransferRequest - * @param ResponseDelegate The response delegate of type - * FImtblPassportResponseDelegate to call on response from JS. - */ - void - ImxBatchNftTransfer(const FImxBatchNftTransferRequest &RequestData, - const FImtblPassportResponseDelegate &ResponseDelegate); - - /** - * - * Checks if the user is registered off-chain. - * @param ResponseDelegate The response delegate of type - * FImtblPassportResponseDelegate to call on response from JS. - */ - void ImxIsRegisteredOffchain(const FImtblPassportResponseDelegate& ResponseDelegate); - - /** - * Register the user to Immutable X if they are not already registered - * @param ResponseDelegate The response delegate of type - * FImtblPassportResponseDelegate to call on response from JS. - */ - void ImxRegisterOffchain(const FImtblPassportResponseDelegate& ResponseDelegate); - - /** - * Checks if the user's credentials have been stored - * @param ResponseDelegate The response delegate of type - * FImtblPassportResponseDelegate to call on response from JS. - */ - void HasStoredCredentials(const FImtblPassportResponseDelegate& ResponseDelegate); + /** + * Initializes the zkEVM provider. + * @param ResponseDelegate The response delegate of type + * FImtblPassportResponseDelegate to call on response from JS. + */ + void ConnectEvm(const FImtblPassportResponseDelegate& ResponseDelegate); + + /** + * This method attempts to authenticate the user and initialises their + * Passport wallet before returning an array of wallet addresses. + * + * This must be called before using other zkEVM functions. + * @param ResponseDelegate The response delegate of type + * FImtblPassportResponseDelegate to call on response from JS. + */ + void ZkEvmRequestAccounts(const FImtblPassportResponseDelegate& ResponseDelegate); + + /** + * Returns the balance of the account of given address in wei. + * @param Data The address and block number for the request + * @param ResponseDelegate The response delegate of type + * FImtblPassportResponseDelegate to call on response from JS. + */ + void ZkEvmGetBalance(const FImmutablePassportZkEvmGetBalanceData& Data, const FImtblPassportResponseDelegate& ResponseDelegate); + + /** + * Creates new message call transaction or a contract creation, if the data + * field contains code. + * @param Request The request data to perform the transaction. + * @param ResponseDelegate The response delegate of type + * FImtblPassportResponseDelegate to call on response from JS. + */ + void ZkEvmSendTransaction(const FImtblTransactionRequest& Request, + const FImtblPassportResponseDelegate& ResponseDelegate); + void GetIdToken(const FImtblPassportResponseDelegate& ResponseDelegate); + void GetAccessToken(const FImtblPassportResponseDelegate& ResponseDelegate); + void GetAddress(const FImtblPassportResponseDelegate& ResponseDelegate); + void GetEmail(const FImtblPassportResponseDelegate& ResponseDelegate); + + /** + * Create a new imx transfer request. + * @param RequestData The transfer details structure of type + * FImxTransferRequest + * @param ResponseDelegate The response delegate of type + * FImtblPassportResponseDelegate to call on response from JS. + */ + void ImxTransfer(const FImxTransferRequest& RequestData, const FImtblPassportResponseDelegate& ResponseDelegate); + + /** + * Creates a new imx batch nft transfer request with the given transfer + * details. + * @param RequestData The transfer details structure of type + * FImxBatchNftTransferRequest + * @param ResponseDelegate The response delegate of type + * FImtblPassportResponseDelegate to call on response from JS. + */ + void ImxBatchNftTransfer(const FImxBatchNftTransferRequest& RequestData, + const FImtblPassportResponseDelegate& ResponseDelegate); + + /** + * + * Checks if the user is registered off-chain. + * @param ResponseDelegate The response delegate of type + * FImtblPassportResponseDelegate to call on response from JS. + */ + void ImxIsRegisteredOffchain(const FImtblPassportResponseDelegate& ResponseDelegate); + + /** + * Register the user to Immutable X if they are not already registered + * @param ResponseDelegate The response delegate of type + * FImtblPassportResponseDelegate to call on response from JS. + */ + void ImxRegisterOffchain(const FImtblPassportResponseDelegate& ResponseDelegate); + + /** + * Checks if the user's credentials have been stored + * @param ResponseDelegate The response delegate of type + * FImtblPassportResponseDelegate to call on response from JS. + */ + void HasStoredCredentials(const FImtblPassportResponseDelegate& ResponseDelegate); protected: - void Setup(TWeakObjectPtr Connector); + void Setup(TWeakObjectPtr Connector); private: - bool bIsInitialized = false; - bool bIsLoggedIn = false; + bool bIsInitialized = false; + bool bIsLoggedIn = false; #if PLATFORM_ANDROID - bool completingPKCE = false; // Used for the PKCE callback + DECLARE_DELEGATE(FImtblPassportOnPKCEDismissedDelegate); + + FImtblPassportOnPKCEDismissedDelegate OnPKCEDismissed; + + bool completingPKCE = false; // Used for the PKCE callback #endif - TWeakObjectPtr JSConnector; - FImmutablePassportInitData InitData; - FDelegateHandle BridgeReadyHandle; - TMap ResponseDelegates; + TWeakObjectPtr JSConnector; + FImmutablePassportInitData InitData; + FDelegateHandle BridgeReadyHandle; + TMap ResponseDelegates; #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC - // Since the second part of PKCE is triggered by deep links, saving the - // response delegate here so it's easier to get - FImtblPassportResponseDelegate PKCEResponseDelegate; + DECLARE_DELEGATE_OneParam(FImtblPassportHandleDeepLinkDelegate, FString); + + FImtblPassportHandleDeepLinkDelegate OnHandleDeepLink; + // Since the second part of PKCE is triggered by deep links, saving the + // response delegate here so it's easier to get + FImtblPassportResponseDelegate PKCEResponseDelegate; + FImtblPassportResponseDelegate PKCELogoutResponseDelegate; + bool IsPKCEConnected = false; #endif - // Ensures that Passport has been initialized before calling JS - bool CheckIsInitialized( - const FString &Action, - const FImtblPassportResponseDelegate &ResponseDelegate) const; - // Calls JS with the given Action and Data, and registers the given - // ResponseDelegate to be called when JS responds - void CallJS(const FString &Action, const FString &Data, - const FImtblPassportResponseDelegate &ClientResponseDelegate, - const FImtblJSResponseDelegate &HandleJSResponse, - const bool bCheckInitialized = true); - // Pulls the ResponseDelegate from the ResponseDelegates map and returns it - TOptional - GetResponseDelegate(const FImtblJSResponse &Response); - void ConfirmCode(const FString &DeviceCode, const float Interval, - const FImtblPassportResponseDelegate &ResponseDelegate); - - void OnInitializeResponse(FImtblJSResponse Response); - void OnLogoutResponse(FImtblJSResponse Response); - void OnConnectResponse(FImtblJSResponse Response); - void OnReconnectResponse(FImtblJSResponse Response); - void OnConnectEvmResponse(FImtblJSResponse Response); - void OnZkEvmRequestAccountsResponse(FImtblJSResponse Response); - void OnZkEvmGetBalanceResponse(FImtblJSResponse Response); - void OnZkEvmSendTransactionResponse(FImtblJSResponse Response); - void OnConfirmCodeResponse(FImtblJSResponse Response); + // Ensures that Passport has been initialized before calling JS + bool CheckIsInitialized(const FString& Action, const FImtblPassportResponseDelegate& ResponseDelegate) const; + // Calls JS with the given Action and Data, and registers the given + // ResponseDelegate to be called when JS responds + void CallJS(const FString& Action, const FString& Data, + const FImtblPassportResponseDelegate& ClientResponseDelegate, + const FImtblJSResponseDelegate& HandleJSResponse, const bool bCheckInitialized = true); + // Pulls the ResponseDelegate from the ResponseDelegates map and returns it + TOptional GetResponseDelegate(const FImtblJSResponse& Response); + void ConfirmCode(const FString& DeviceCode, const float Interval, + const FImtblPassportResponseDelegate& ResponseDelegate); + + void OnInitializeResponse(FImtblJSResponse Response); + void OnLogoutResponse(FImtblJSResponse Response); + void OnConnectResponse(FImtblJSResponse Response); + void OnConnectSilentResponse(FImtblJSResponse Response); + void OnConnectEvmResponse(FImtblJSResponse Response); + void OnZkEvmRequestAccountsResponse(FImtblJSResponse Response); + void OnZkEvmGetBalanceResponse(FImtblJSResponse Response); + void OnZkEvmSendTransactionResponse(FImtblJSResponse Response); + void OnConfirmCodeResponse(FImtblJSResponse Response); #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC - void OnGetPKCEAuthUrlResponse(FImtblJSResponse Response); - void OnConnectPKCEResponse(FImtblJSResponse Response); + void OnGetPKCEAuthUrlResponse(FImtblJSResponse Response); + void OnConnectPKCEResponse(FImtblJSResponse Response); #endif - void OnGetIdTokenResponse(FImtblJSResponse Response); - void OnGetAccessTokenResponse(FImtblJSResponse Response); - void OnGetAddressResponse(FImtblJSResponse Response); - void OnGetEmailResponse(FImtblJSResponse Response); - void OnTransferResponse(FImtblJSResponse Response); - void OnBatchNftTransferResponse(FImtblJSResponse Response); - void OnImxIsRegisteredOffchain(FImtblJSResponse Response); - void OnImxRegisterOffchain(FImtblJSResponse Response); + void OnGetIdTokenResponse(FImtblJSResponse Response); + void OnGetAccessTokenResponse(FImtblJSResponse Response); + void OnGetAddressResponse(FImtblJSResponse Response); + void OnGetEmailResponse(FImtblJSResponse Response); + void OnTransferResponse(FImtblJSResponse Response); + void OnBatchNftTransferResponse(FImtblJSResponse Response); + void OnImxIsRegisteredOffchain(FImtblJSResponse Response); + void OnImxRegisterOffchain(FImtblJSResponse Response); - void LogAndIgnoreResponse(FImtblJSResponse Response); + void LogAndIgnoreResponse(FImtblJSResponse Response); #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC - void OnDeepLinkActivated(FString DeepLink); - void CompletePKCEFlow(FString Url); + void OnDeepLinkActivated(FString DeepLink); + void CompleteLoginPKCEFlow(FString Url); #endif #if PLATFORM_ANDROID - void HandleOnPKCEDismissed(); - void CallJniStaticVoidMethod(JNIEnv *Env, const jclass Class, - jmethodID Method, ...); + void HandleOnLoginPKCEDismissed(); + void CallJniStaticVoidMethod(JNIEnv* Env, const jclass Class, jmethodID Method, ...); + void LaunchAndroidUrl(FString Url); #endif }; -