diff --git a/Content/BlueprintSampleContent/ImtblAuthenticatedWidget4_26.uasset b/Content/BlueprintSampleContent/ImtblAuthenticatedWidget4_26.uasset index 0548998..f6a40f5 100644 Binary files a/Content/BlueprintSampleContent/ImtblAuthenticatedWidget4_26.uasset and b/Content/BlueprintSampleContent/ImtblAuthenticatedWidget4_26.uasset differ diff --git a/Content/BlueprintSampleContent/ImtblEvmSignTypedDataWidget4_27.uasset b/Content/BlueprintSampleContent/ImtblEvmSignTypedDataWidget4_27.uasset new file mode 100644 index 0000000..044339d Binary files /dev/null and b/Content/BlueprintSampleContent/ImtblEvmSignTypedDataWidget4_27.uasset differ diff --git a/Source/Immutable/Private/Immutable/Actions/ImtblPassportZkEvmSignTypedDataV4AsyncAction.cpp b/Source/Immutable/Private/Immutable/Actions/ImtblPassportZkEvmSignTypedDataV4AsyncAction.cpp new file mode 100644 index 0000000..67f79b3 --- /dev/null +++ b/Source/Immutable/Private/Immutable/Actions/ImtblPassportZkEvmSignTypedDataV4AsyncAction.cpp @@ -0,0 +1,53 @@ +#include "Immutable/Actions/ImtblPassportZkEvmSignTypedDataV4AsyncAction.h" + +#include "Immutable/ImmutablePassport.h" +#include "Immutable/ImmutableSubsystem.h" +#include "Immutable/Misc/ImtblLogging.h" + + +UImtblPassportZkEvmSignTypedDataV4AsyncAction* UImtblPassportZkEvmSignTypedDataV4AsyncAction::ZkEvmSignTypedDataV4(UObject* WorldContextObject, const FString& JsonStringRequest) +{ + UImtblPassportZkEvmSignTypedDataV4AsyncAction* PassportZkEvmSignTypedDataV4BlueprintNode = NewObject(); + + PassportZkEvmSignTypedDataV4BlueprintNode->WorldContextObject = WorldContextObject; + PassportZkEvmSignTypedDataV4BlueprintNode->JsonStringSignRequest = JsonStringRequest; + + return PassportZkEvmSignTypedDataV4BlueprintNode; +} + +void UImtblPassportZkEvmSignTypedDataV4AsyncAction::Activate() +{ + if (!WorldContextObject || !WorldContextObject->GetWorld()) + { + FString Err = "zkEVM Sign Typed Data V4 failed due to missing world or world " "context object."; + IMTBL_WARN("%s", *Err) + Failed.Broadcast(Err, TEXT("")); + return; + } + + GetSubsystem()->WhenReady(this, &UImtblPassportZkEvmSignTypedDataV4AsyncAction::DoZkEvmSignTypedDataV4); +} + +void UImtblPassportZkEvmSignTypedDataV4AsyncAction::DoZkEvmSignTypedDataV4(TWeakObjectPtr JSConnector) +{ + auto Passport = GetSubsystem()->GetPassport(); + + if (Passport.IsValid()) + { + Passport->ZkEvmSignTypedDataV4(JsonStringSignRequest, UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject(this, &UImtblPassportZkEvmSignTypedDataV4AsyncAction::OnZkEvmSignTypedDataV4Response)); + } +} + +void UImtblPassportZkEvmSignTypedDataV4AsyncAction::OnZkEvmSignTypedDataV4Response(FImmutablePassportResult Result) +{ + if (Result.Success) + { + IMTBL_LOG("zkEVM Sign Typed Data V4 success") + MessageSigned.Broadcast(TEXT(""), UImmutablePassport::GetResponseResultAsString(Result.Response)); + } + else + { + IMTBL_LOG("zkEVM Sign Typed Data V4 failed") + Failed.Broadcast(Result.Error, TEXT("")); + } +} diff --git a/Source/Immutable/Private/Immutable/ImmutablePassport.cpp b/Source/Immutable/Private/Immutable/ImmutablePassport.cpp index ff6c4ab..b70e6e2 100644 --- a/Source/Immutable/Private/Immutable/ImmutablePassport.cpp +++ b/Source/Immutable/Private/Immutable/ImmutablePassport.cpp @@ -130,6 +130,11 @@ void UImmutablePassport::ZkEvmGetTransactionReceipt(const FZkEvmTransactionRecei CallJS(ImmutablePassportAction::ZkEvmGetTransactionReceipt, UStructToJsonString(Request), ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnBridgeCallbackResponse)); } +void UImmutablePassport::ZkEvmSignTypedDataV4(const FString& RequestJsonString, const FImtblPassportResponseDelegate& ResponseDelegate) +{ + CallJS(ImmutablePassportAction::ZkEvmSignTypedDataV4, RequestJsonString, ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnBridgeCallbackResponse)); +} + void UImmutablePassport::ConfirmCode(const FString& DeviceCode, const float Interval, const FImtblPassportResponseDelegate& ResponseDelegate) { FImmutablePassportCodeConfirmRequestData Data{DeviceCode, Interval}; diff --git a/Source/Immutable/Public/Immutable/Actions/ImtblPassportZkEvmSignTypedDataV4AsyncAction.h b/Source/Immutable/Public/Immutable/Actions/ImtblPassportZkEvmSignTypedDataV4AsyncAction.h new file mode 100644 index 0000000..cd11b98 --- /dev/null +++ b/Source/Immutable/Public/Immutable/Actions/ImtblPassportZkEvmSignTypedDataV4AsyncAction.h @@ -0,0 +1,35 @@ +#pragma once + + +#include "Immutable/ImmutablePassport.h" +#include "ImtblBlueprintAsyncAction.h" + +#include "ImtblPassportZkEvmSignTypedDataV4AsyncAction.generated.h" + +/** + * Async action blueprint node for zkEVM SignTypedDataV4 + */ +UCLASS() +class IMMUTABLE_API UImtblPassportZkEvmSignTypedDataV4AsyncAction : public UImtblBlueprintAsyncAction +{ + GENERATED_BODY() + + DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FPassportZkEvmSignTypedDataV4OutputPin, FString, ErrorMessage, FString, Signature); + +public: + UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", BlueprintInternalUseOnly = "true"), Category = "Immutable") + static UImtblPassportZkEvmSignTypedDataV4AsyncAction* ZkEvmSignTypedDataV4(UObject* WorldContextObject, const FString& JsonStringRequest); + + virtual void Activate() override; + +private: + FString JsonStringSignRequest; + + UPROPERTY(BlueprintAssignable) + FPassportZkEvmSignTypedDataV4OutputPin MessageSigned; + UPROPERTY(BlueprintAssignable) + FPassportZkEvmSignTypedDataV4OutputPin Failed; + + void DoZkEvmSignTypedDataV4(TWeakObjectPtr JSGetConnector); + void OnZkEvmSignTypedDataV4Response(FImmutablePassportResult Result); +}; diff --git a/Source/Immutable/Public/Immutable/ImmutableNames.h b/Source/Immutable/Public/Immutable/ImmutableNames.h index 8e87a6c..7183e94 100644 --- a/Source/Immutable/Public/Immutable/ImmutableNames.h +++ b/Source/Immutable/Public/Immutable/ImmutableNames.h @@ -16,6 +16,7 @@ namespace ImmutablePassportAction const FString ZkEvmSendTransaction = TEXT("zkEvmSendTransaction"); const FString zkEvmSendTransactionWithConfirmation = TEXT("zkEvmSendTransactionWithConfirmation"); const FString ZkEvmGetTransactionReceipt = TEXT("zkEvmGetTransactionReceipt"); + const FString ZkEvmSignTypedDataV4 = TEXT("zkEvmSignTypedDataV4"); #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC const FString GetPKCEAuthUrl = TEXT("getPKCEAuthUrl"); diff --git a/Source/Immutable/Public/Immutable/ImmutablePassport.h b/Source/Immutable/Public/Immutable/ImmutablePassport.h index 5a555d8..a7a2fda 100644 --- a/Source/Immutable/Public/Immutable/ImmutablePassport.h +++ b/Source/Immutable/Public/Immutable/ImmutablePassport.h @@ -4,7 +4,6 @@ #include "CoreMinimal.h" #include "JsonObjectConverter.h" -#include "Misc/EngineVersion.h" #include "Runtime/Core/Public/HAL/Platform.h" #include "UObject/Object.h" #include "Immutable/ImtblJSConnector.h" @@ -164,6 +163,16 @@ class IMMUTABLE_API UImmutablePassport : public UObject * FImtblPassportResponseDelegate to call on response from JS. */ void ZkEvmGetTransactionReceipt(const FZkEvmTransactionReceiptRequest& Request, const FImtblPassportResponseDelegate& ResponseDelegate); + + /** + * Generate a signature for a typed data V4 object + * Signs the EIP-712 structured message in JSON string format using the logged-in Passport account. + * @see https://eips.ethereum.org/EIPS/eip-712 + * @param RequestJsonString The EIP-712 structured data in JSON string format + * @param ResponseDelegate The response delegate of type + * FImtblPassportResponseDelegate to call on response from JS. + */ + void ZkEvmSignTypedDataV4(const FString& RequestJsonString, const FImtblPassportResponseDelegate& ResponseDelegate); /** * Gets the currently saved ID token without verifying its validity.