Skip to content

Commit cea584b

Browse files
committed
feat: add sign typed data v4 method
1 parent 8203fad commit cea584b

File tree

7 files changed

+325
-0
lines changed

7 files changed

+325
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Fill out your copyright notice in the Description page of Project Settings.
2+
3+
#include "Immutable/Actions/ImtblPassportZkEvmSignTypedDataV4AsyncAction.h"
4+
5+
#include "Immutable/ImmutablePassport.h"
6+
#include "Immutable/ImmutableSubsystem.h"
7+
#include "Immutable/Misc/ImtblLogging.h"
8+
9+
UImtblPassportZkEvmSignTypedDataV4AsyncAction* UImtblPassportZkEvmSignTypedDataV4AsyncAction::ZkEvmSignTypedDataV4(UObject* WorldContextObject, const FZkEvmSignTypedDataV4Request& Request)
10+
{
11+
UImtblPassportZkEvmSignTypedDataV4AsyncAction* PassportZkEvmSignTypedDataV4BlueprintNode = NewObject<UImtblPassportZkEvmSignTypedDataV4AsyncAction>();
12+
PassportZkEvmSignTypedDataV4BlueprintNode->WorldContextObject = WorldContextObject;
13+
PassportZkEvmSignTypedDataV4BlueprintNode->SignRequest = Request;
14+
return PassportZkEvmSignTypedDataV4BlueprintNode;
15+
}
16+
17+
void UImtblPassportZkEvmSignTypedDataV4AsyncAction::Activate()
18+
{
19+
if (!WorldContextObject || !WorldContextObject->GetWorld())
20+
{
21+
FString Err = "zkEVM Sign Typed Data V4 failed due to missing world or world " "context object.";
22+
IMTBL_WARN("%s", *Err)
23+
Failed.Broadcast(Err, TEXT(""));
24+
return;
25+
}
26+
27+
GetSubsystem()->WhenReady(this, &UImtblPassportZkEvmSignTypedDataV4AsyncAction::DoZkEvmSignTypedDataV4);
28+
}
29+
30+
void UImtblPassportZkEvmSignTypedDataV4AsyncAction::DoZkEvmSignTypedDataV4(TWeakObjectPtr<UImtblJSConnector> JSConnector)
31+
{
32+
auto Passport = GetSubsystem()->GetPassport();
33+
34+
if (Passport.IsValid())
35+
{
36+
Passport->ZkEvmSignTypedDataV4(SignRequest, UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject(this, &UImtblPassportZkEvmSignTypedDataV4AsyncAction::OnZkEvmSignTypedDataV4Response));
37+
}
38+
}
39+
40+
void UImtblPassportZkEvmSignTypedDataV4AsyncAction::OnZkEvmSignTypedDataV4Response(FImmutablePassportResult Result)
41+
{
42+
if (Result.Success)
43+
{
44+
IMTBL_LOG("zkEVM Sign Typed Data V4 success")
45+
MessageSigned.Broadcast(TEXT(""), UImmutablePassport::GetResponseResultAsString(Result.Response));
46+
}
47+
else
48+
{
49+
IMTBL_LOG("zkEVM Sign Typed Data V4 failed")
50+
Failed.Broadcast(Result.Error, TEXT(""));
51+
}
52+
}

Source/Immutable/Private/Immutable/ImmutablePassport.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Immutable/ImmutableSaveGame.h"
1111
#include "Kismet/GameplayStatics.h"
1212
#include "Policies/CondensedJsonPrintPolicy.h"
13+
#include "Json.h"
1314

1415
#if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC
1516
#include "GenericPlatform/GenericPlatformHttp.h"
@@ -27,6 +28,34 @@
2728

2829
#define PASSPORT_SAVE_GAME_SLOT_NAME TEXT("Immutable")
2930

31+
TSharedPtr<FJsonObject> UStructToJsonObject(const UStruct* Struct, const void* StructData)
32+
{
33+
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
34+
35+
if (FJsonObjectConverter::UStructToJsonObject(Struct, StructData, JsonObject.ToSharedRef(), 0, 0))
36+
{
37+
return JsonObject;
38+
}
39+
40+
return nullptr; // Return nullptr if conversion fails
41+
}
42+
43+
TArray<TSharedPtr<FJsonValue>> ConvertNameTypeToJsonArray(const TArray<FZkEvmSignTypedDataV4NameType>& NameTypes)
44+
{
45+
TArray<TSharedPtr<FJsonValue>> NameTypeArray;
46+
47+
for (const FZkEvmSignTypedDataV4NameType& Item : NameTypes)
48+
{
49+
TSharedPtr<FJsonObject> NameTypeItemObject = MakeShareable(new FJsonObject());
50+
NameTypeItemObject->SetStringField("name", Item.Name);
51+
NameTypeItemObject->SetStringField("type", Item.Type);
52+
53+
NameTypeArray.Add(MakeShareable(new FJsonValueObject(NameTypeItemObject)));
54+
}
55+
56+
return NameTypeArray;
57+
}
58+
3059
void UImmutablePassport::Initialize(const FImmutablePassportInitData& Data, const FImtblPassportResponseDelegate& ResponseDelegate)
3160
{
3261
check(JSConnector.IsValid());
@@ -130,6 +159,37 @@ void UImmutablePassport::ZkEvmGetTransactionReceipt(const FZkEvmTransactionRecei
130159
CallJS(ImmutablePassportAction::ZkEvmGetTransactionReceipt, UStructToJsonString(Request), ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnBridgeCallbackResponse));
131160
}
132161

162+
void UImmutablePassport::ZkEvmSignTypedDataV4(const FZkEvmSignTypedDataV4Request& Request, const FImtblPassportResponseDelegate& ResponseDelegate)
163+
{
164+
// this messy and manual JSON conversion is because the UR4 JSON util automatically downcases the first letter of
165+
// property names. The SignTypedDataV4 method on the game bridge side requires the `Request.types` properties
166+
// to be capitalised, otherwise the TS Passport module will throw a validation erro.
167+
TSharedPtr<FJsonObject> DomainJsonObject = UStructToJsonObject(FZkEvmSignTypedDataV4Domain::StaticStruct(), &Request.domain);
168+
TSharedPtr<FJsonObject> MessageJsonObject = UStructToJsonObject(FZkEvmSignTypedDataV4Message::StaticStruct(), &Request.message);
169+
170+
TArray<TSharedPtr<FJsonValue>> OrderComponentsArray = ConvertNameTypeToJsonArray(Request.types.OrderComponents);
171+
TArray<TSharedPtr<FJsonValue>> ConsiderationItemArray = ConvertNameTypeToJsonArray(Request.types.ConsiderationItem);
172+
TArray<TSharedPtr<FJsonValue>> OfferItemArray = ConvertNameTypeToJsonArray(Request.types.OfferItem);
173+
TArray<TSharedPtr<FJsonValue>> EIP712DomainArray = ConvertNameTypeToJsonArray(Request.types.EIP712Domain);
174+
TSharedPtr<FJsonObject> TypesJsonObject = MakeShareable(new FJsonObject());
175+
TypesJsonObject->SetArrayField("OrderComponents", OrderComponentsArray);
176+
TypesJsonObject->SetArrayField("ConsiderationItem", ConsiderationItemArray);
177+
TypesJsonObject->SetArrayField("OfferItem", OfferItemArray);
178+
TypesJsonObject->SetArrayField("EIP712Domain", EIP712DomainArray);
179+
180+
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
181+
JsonObject->SetObjectField("domain", DomainJsonObject);
182+
JsonObject->SetObjectField("types", TypesJsonObject);
183+
JsonObject->SetObjectField("message", MessageJsonObject);
184+
JsonObject->SetStringField("primaryType", Request.primaryType);
185+
186+
FString JsonString;
187+
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&JsonString);
188+
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
189+
190+
CallJS(ImmutablePassportAction::ZkEvmSignTypedDataV4, JsonString, ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnBridgeCallbackResponse));
191+
}
192+
133193
void UImmutablePassport::ConfirmCode(const FString& DeviceCode, const float Interval, const FImtblPassportResponseDelegate& ResponseDelegate)
134194
{
135195
FImmutablePassportCodeConfirmRequestData Data{DeviceCode, Interval};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Fill out your copyright notice in the Description page of Project Settings.
2+
3+
#pragma once
4+
5+
#include "CoreMinimal.h"
6+
#include "Immutable/ImmutablePassport.h"
7+
#include "ImtblBlueprintAsyncAction.h"
8+
#include "ImtblPassportZkEvmSignTypedDataV4AsyncAction.generated.h"
9+
10+
/**
11+
* Async action blueprint node for zkEVM Send Transaction
12+
*/
13+
UCLASS()
14+
class IMMUTABLE_API UImtblPassportZkEvmSignTypedDataV4AsyncAction : public UImtblBlueprintAsyncAction
15+
{
16+
GENERATED_BODY()
17+
18+
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FPassportZkEvmSignTypedDataV4OutputPin, FString, ErrorMessage, FString, Signature);
19+
20+
public:
21+
UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", BlueprintInternalUseOnly = "true"), Category = "Immutable")
22+
static UImtblPassportZkEvmSignTypedDataV4AsyncAction* ZkEvmSignTypedDataV4(UObject* WorldContextObject, const FZkEvmSignTypedDataV4Request& Request);
23+
24+
virtual void Activate() override;
25+
26+
private:
27+
FZkEvmSignTypedDataV4Request SignRequest;
28+
29+
UPROPERTY(BlueprintAssignable)
30+
FPassportZkEvmSignTypedDataV4OutputPin MessageSigned;
31+
UPROPERTY(BlueprintAssignable)
32+
FPassportZkEvmSignTypedDataV4OutputPin Failed;
33+
34+
void DoZkEvmSignTypedDataV4(TWeakObjectPtr<class UImtblJSConnector> JSGetConnector);
35+
void OnZkEvmSignTypedDataV4Response(FImmutablePassportResult Result);
36+
};

Source/Immutable/Public/Immutable/ImmutableDataTypes.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,12 @@ struct IMMUTABLE_API FZkEvmTransactionReceipt
276276
UPROPERTY()
277277
FString type;
278278
};
279+
280+
USTRUCT(BlueprintType)
281+
struct FZkEvmSignTypedDataV4Response
282+
{
283+
GENERATED_BODY()
284+
285+
UPROPERTY()
286+
FString signature;
287+
};

Source/Immutable/Public/Immutable/ImmutableNames.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace ImmutablePassportAction
1616
const FString ZkEvmSendTransaction = TEXT("zkEvmSendTransaction");
1717
const FString zkEvmSendTransactionWithConfirmation = TEXT("zkEvmSendTransactionWithConfirmation");
1818
const FString ZkEvmGetTransactionReceipt = TEXT("zkEvmGetTransactionReceipt");
19+
const FString ZkEvmSignTypedDataV4 = TEXT("zkEvmSignTypedDataV4");
1920

2021
#if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC
2122
const FString GetPKCEAuthUrl = TEXT("getPKCEAuthUrl");

Source/Immutable/Public/Immutable/ImmutablePassport.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ class IMMUTABLE_API UImmutablePassport : public UObject
115115
* FImtblPassportResponseDelegate to call on response from JS.
116116
*/
117117
void ZkEvmGetTransactionReceipt(const FZkEvmTransactionReceiptRequest& Request, const FImtblPassportResponseDelegate& ResponseDelegate);
118+
119+
/**
120+
* Generate a signature for a typed data V4 object
121+
* @param Request Type data to sign
122+
* @param ResponseDelegate The response delegate of type FImtblPassportResponseDelegate to call on response from JS.
123+
*/
124+
void ZkEvmSignTypedDataV4(const FZkEvmSignTypedDataV4Request& Request, const FImtblPassportResponseDelegate& ResponseDelegate);
118125

119126
void GetIdToken(const FImtblPassportResponseDelegate& ResponseDelegate);
120127
void GetAccessToken(const FImtblPassportResponseDelegate& ResponseDelegate);

Source/Immutable/Public/Immutable/ImmutableRequests.h

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,163 @@ struct IMMUTABLE_API FZkEvmTransactionReceiptRequest
6262
UPROPERTY()
6363
FString txHash;
6464
};
65+
66+
USTRUCT(BlueprintType)
67+
struct IMMUTABLE_API FZkEvmSignTypedDataV4Domain
68+
{
69+
GENERATED_BODY()
70+
71+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
72+
FString ChainId;
73+
74+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
75+
FString Name;
76+
77+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
78+
FString VerifyingContract;
79+
80+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
81+
FString Version;
82+
};
83+
84+
USTRUCT(BlueprintType)
85+
struct FZkEvmSignTypedDataV4NameType
86+
{
87+
GENERATED_BODY()
88+
89+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
90+
FString Name;
91+
92+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
93+
FString Type;
94+
};
95+
96+
USTRUCT(BlueprintType)
97+
struct FZkEvmSignTypedDataV4SignableMessageTypes
98+
{
99+
GENERATED_BODY()
100+
101+
// JsonFieldName metadata specifier is used to define the exact name that should be used in the JSON output
102+
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (JsonFieldName = "OrderComponents"))
103+
TArray<FZkEvmSignTypedDataV4NameType> OrderComponents;
104+
105+
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (JsonFieldName = "OfferItem"))
106+
TArray<FZkEvmSignTypedDataV4NameType> OfferItem;
107+
108+
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (JsonFieldName = "ConsiderationItem"))
109+
TArray<FZkEvmSignTypedDataV4NameType> ConsiderationItem;
110+
111+
UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (JsonFieldName = "EIP712Domain"))
112+
TArray<FZkEvmSignTypedDataV4NameType> EIP712Domain;
113+
};
114+
115+
USTRUCT(BlueprintType)
116+
struct FZkEvmSignTypedDataV4OfferItem
117+
{
118+
GENERATED_BODY()
119+
120+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
121+
uint8 ItemType;
122+
123+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
124+
FString Token;
125+
126+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
127+
FString IdentifierOrCriteria;
128+
129+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
130+
FString StartAmount;
131+
132+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
133+
FString EndAmount;
134+
};
135+
136+
USTRUCT(BlueprintType)
137+
struct FZkEvmSignTypedDataV4ConsiderationItem
138+
{
139+
GENERATED_BODY()
140+
141+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
142+
uint8 ItemType;
143+
144+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
145+
FString Token;
146+
147+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
148+
FString IdentifierOrCriteria;
149+
150+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
151+
FString StartAmount;
152+
153+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
154+
FString EndAmount;
155+
156+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
157+
FString Recipient;
158+
};
159+
160+
USTRUCT(BlueprintType)
161+
struct FZkEvmSignTypedDataV4Message
162+
{
163+
GENERATED_BODY()
164+
165+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
166+
FString Offerer;
167+
168+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
169+
FString Zone;
170+
171+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
172+
TArray<FZkEvmSignTypedDataV4OfferItem> Offer;
173+
174+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
175+
TArray<FZkEvmSignTypedDataV4ConsiderationItem> Consideration;
176+
177+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
178+
uint8 OrderType;
179+
180+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
181+
FString StartTime;
182+
183+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
184+
FString EndTime;
185+
186+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
187+
FString ZoneHash;
188+
189+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
190+
FString Salt;
191+
192+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
193+
FString ConduitKey;
194+
195+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
196+
FString Counter;
197+
};
198+
199+
USTRUCT(BlueprintType)
200+
struct IMMUTABLE_API FZkEvmSignTypedDataV4Request
201+
{
202+
GENERATED_BODY()
203+
204+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
205+
FZkEvmSignTypedDataV4Domain domain;
206+
207+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
208+
FZkEvmSignTypedDataV4SignableMessageTypes types;
209+
210+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
211+
FZkEvmSignTypedDataV4Message message;
212+
213+
UPROPERTY(BlueprintReadWrite, EditAnywhere)
214+
FString primaryType;
215+
};
216+
217+
// USTRUCT(BlueprintType)
218+
// struct IMMUTABLE_API FZkEvmSignTypedDataV4Request
219+
// {
220+
// GENERATED_BODY()
221+
//
222+
// UPROPERTY(BlueprintReadWrite, EditAnywhere)
223+
// FSignableMessage message;
224+
// };

0 commit comments

Comments
 (0)