From 7fc3b7bdc2587c94815b3f87d2ea21b1d547e282 Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Tue, 29 Jul 2025 19:03:45 +0700 Subject: [PATCH] Fix CreateSessionKey ApprovedTargets > 0 Crash Fixes memory crash when passing string arrays to native library due to temporary objects being destroyed before async calls by switching to persistent FAnsiString containers to ensure stable memory addresses throughout the async operation lifecycle. --- .../Wallets/ThirdwebSmartWalletHandle.cpp | 344 +++++++++--------- 1 file changed, 178 insertions(+), 166 deletions(-) diff --git a/Source/Thirdweb/Private/Wallets/ThirdwebSmartWalletHandle.cpp b/Source/Thirdweb/Private/Wallets/ThirdwebSmartWalletHandle.cpp index 64afd5b..e197d5e 100644 --- a/Source/Thirdweb/Private/Wallets/ThirdwebSmartWalletHandle.cpp +++ b/Source/Thirdweb/Private/Wallets/ThirdwebSmartWalletHandle.cpp @@ -40,7 +40,7 @@ void FSmartWalletHandle::Create(const FInAppWalletHandle& InInAppWallet, const bool bGasless, const FString& Factory, const FString& AccountOverride, - const FString& EntryPoint, + const FString& EntryPoint, const FCreateSmartWalletDelegate& SuccessDelegate, const FStringDelegate& ErrorDelegate) { @@ -55,32 +55,32 @@ void FSmartWalletHandle::Create(const FInAppWalletHandle& InInAppWallet, return; } - UE::Tasks::Launch(UE_SOURCE_LOCATION, [InInAppWallet, ChainID, bGasless, Factory, AccountOverride, EntryPoint, SuccessDelegate, ErrorDelegate] - { - if (FString Error; Thirdweb::create_smart_wallet( - TO_RUST_STRING(UThirdwebRuntimeSettings::GetClientId()), - TO_RUST_STRING(UThirdwebRuntimeSettings::GetBundleId()), - nullptr, - InInAppWallet.GetID(), - TO_RUST_STRING(FString::Printf(TEXT("%lld"), ChainID)), - bGasless, - TO_RUST_STRING(Factory), - TO_RUST_STRING(AccountOverride), - TO_RUST_STRING(EntryPoint) - ).AssignResult(Error)) - { - const FSmartWalletHandle SmartWallet = FSmartWalletHandle(InInAppWallet, Error, Factory); - SuccessDelegate.Execute(SmartWallet); - ThirdwebUtils::Internal::SendConnectEvent(SmartWallet); - } - else - { - if (ErrorDelegate.IsBound()) - { - ErrorDelegate.Execute(Error); - } - } - }); + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [InInAppWallet, ChainID, bGasless, Factory, AccountOverride, EntryPoint, SuccessDelegate, ErrorDelegate] + { + if (FString Error; Thirdweb::create_smart_wallet( + TO_RUST_STRING(UThirdwebRuntimeSettings::GetClientId()), + TO_RUST_STRING(UThirdwebRuntimeSettings::GetBundleId()), + nullptr, + InInAppWallet.GetID(), + TO_RUST_STRING(FString::Printf(TEXT("%lld"), ChainID)), + bGasless, + TO_RUST_STRING(Factory), + TO_RUST_STRING(AccountOverride), + TO_RUST_STRING(EntryPoint)).AssignResult(Error)) + { + const FSmartWalletHandle SmartWallet = FSmartWalletHandle(InInAppWallet, Error, Factory); + SuccessDelegate.Execute(SmartWallet); + ThirdwebUtils::Internal::SendConnectEvent(SmartWallet); + } + else + { + if (ErrorDelegate.IsBound()) + { + ErrorDelegate.Execute(Error); + } + } + }); } void FSmartWalletHandle::IsDeployed(const FBoolDelegate& SuccessDelegate, const FStringDelegate& ErrorDelegate) @@ -89,38 +89,33 @@ void FSmartWalletHandle::IsDeployed(const FBoolDelegate& SuccessDelegate, const CHECK_VALIDITY(ErrorDelegate); FSmartWalletHandle ThisCopy = *this; - UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, SuccessDelegate, ErrorDelegate] - { - if (FString Error; Thirdweb::smart_wallet_is_deployed(ThisCopy.GetID()).AssignResult(Error)) - { - SuccessDelegate.Execute(Error.ToBool()); - } - else - { - ErrorDelegate.Execute(Error); - } - }); + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [ThisCopy, SuccessDelegate, ErrorDelegate] + { + if (FString Error; Thirdweb::smart_wallet_is_deployed(ThisCopy.GetID()).AssignResult(Error)) + { + SuccessDelegate.Execute(Error.ToBool()); + } + else + { + ErrorDelegate.Execute(Error); + } + }); } -void FSmartWalletHandle::CreateSessionKey( - const FString& Signer, - const TArray& ApprovedTargets, - const FString& NativeTokenLimitPerTransactionInWei, - const FDateTime& PermissionEnd, - const FStringDelegate& SuccessDelegate, - const FStringDelegate& ErrorDelegate -) +void FSmartWalletHandle::CreateSessionKey(const FString& Signer, + const TArray& ApprovedTargets, + const FString& NativeTokenLimitPerTransactionInWei, + const FDateTime& PermissionEnd, + const FStringDelegate& SuccessDelegate, + const FStringDelegate& ErrorDelegate) { CHECK_DELEGATES(SuccessDelegate, ErrorDelegate); CHECK_VALIDITY(ErrorDelegate); FDateTime TenYearsFromNow = FDateTime::UtcNow() + FTimespan::FromDays(10 * 365); FDateTime EndTime = TenYearsFromNow; - TArray ApprovedTargetsCArray; - for (const FString& Target : ApprovedTargets) - { - ApprovedTargetsCArray.Add(TO_RUST_STRING(Target)); - } + if (PermissionEnd.ToUnixTimestamp() > 0) { EndTime = PermissionEnd; @@ -130,42 +125,54 @@ void FSmartWalletHandle::CreateSessionKey( return; } } + FSmartWalletHandle ThisCopy = *this; - UE::Tasks::Launch( - UE_SOURCE_LOCATION, - [ThisCopy, Signer, ApprovedTargets, ApprovedTargetsCArray, NativeTokenLimitPerTransactionInWei, EndTime, TenYearsFromNow, SuccessDelegate, - ErrorDelegate] - { - if (FString Error; Thirdweb::smart_wallet_create_session_key( - ThisCopy.GetID(), - TO_RUST_STRING(Signer), - ApprovedTargets.IsEmpty() ? nullptr : ApprovedTargetsCArray.GetData(), - ApprovedTargetsCArray.Num(), - TO_RUST_STRING(NativeTokenLimitPerTransactionInWei), - 0, - TO_RUST_TIMESTAMP(EndTime), - 0, - TO_RUST_TIMESTAMP(TenYearsFromNow) - ).AssignResult(Error)) - { - TSharedPtr JsonObject = MakeShareable(new FJsonObject); - const TSharedRef> Reader = TJsonReaderFactory<>::Create(Error); - if (FJsonSerializer::Deserialize(Reader, JsonObject); JsonObject.IsValid()) - { - if (JsonObject->HasTypedField(TEXT("transactionHash"))) - { - SuccessDelegate.Execute(JsonObject->GetStringField(TEXT("transactionHash"))); - } - } - } - else - { - if (ErrorDelegate.IsBound()) - { - ErrorDelegate.Execute(Error); - } - } - }); + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [ThisCopy, Signer, ApprovedTargets, NativeTokenLimitPerTransactionInWei, EndTime, TenYearsFromNow, SuccessDelegate, ErrorDelegate] + { + // FAnsiString maintains pointer stability even when moved + TArray ApprovedTargetsCopy = ApprovedTargets; + TArray ApprovedTargetsAnsi; + TArray ApprovedTargetsCArray; + + for (const FString& Target : ApprovedTargetsCopy) + { + FAnsiString AnsiTarget = TCHAR_TO_ANSI(*Target); + ApprovedTargetsAnsi.Add(AnsiTarget); + ApprovedTargetsCArray.Add(*ApprovedTargetsAnsi.Last()); + } + + + if (FString Error; Thirdweb::smart_wallet_create_session_key(ThisCopy.GetID(), + TO_RUST_STRING(Signer), + ApprovedTargetsCArray.IsEmpty() + ? nullptr + : ApprovedTargetsCArray.GetData(), + ApprovedTargetsCArray.Num(), + TO_RUST_STRING(NativeTokenLimitPerTransactionInWei), + 0, + TO_RUST_TIMESTAMP(EndTime), + 0, + TO_RUST_TIMESTAMP(TenYearsFromNow)).AssignResult(Error)) + { + TSharedPtr JsonObject = MakeShareable(new FJsonObject); + const TSharedRef> Reader = TJsonReaderFactory<>::Create(Error); + if (FJsonSerializer::Deserialize(Reader, JsonObject); JsonObject.IsValid()) + { + if (JsonObject->HasTypedField(TEXT("transactionHash"))) + { + SuccessDelegate.Execute(JsonObject->GetStringField(TEXT("transactionHash"))); + } + } + } + else + { + if (ErrorDelegate.IsBound()) + { + ErrorDelegate.Execute(Error); + } + } + }); } void FSmartWalletHandle::RevokeSessionKey(const FString& Signer, const FSimpleDelegate& SuccessDelegate, const FStringDelegate& ErrorDelegate) @@ -174,17 +181,18 @@ void FSmartWalletHandle::RevokeSessionKey(const FString& Signer, const FSimpleDe CHECK_VALIDITY(ErrorDelegate); FSmartWalletHandle ThisCopy = *this; - UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, Signer, SuccessDelegate, ErrorDelegate] - { - if (FString Error; Thirdweb::smart_wallet_revoke_session_key(ThisCopy.GetID(), TO_RUST_STRING(Signer)).AssignResult(Error)) - { - SuccessDelegate.Execute(); - } - else - { - ErrorDelegate.Execute(Error); - } - }); + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [ThisCopy, Signer, SuccessDelegate, ErrorDelegate] + { + if (FString Error; Thirdweb::smart_wallet_revoke_session_key(ThisCopy.GetID(), TO_RUST_STRING(Signer)).AssignResult(Error)) + { + SuccessDelegate.Execute(); + } + else + { + ErrorDelegate.Execute(Error); + } + }); } void FSmartWalletHandle::GetAdmins(const FStringArrayDelegate& SuccessDelegate, const FStringDelegate& ErrorDelegate) @@ -193,31 +201,32 @@ void FSmartWalletHandle::GetAdmins(const FStringArrayDelegate& SuccessDelegate, CHECK_VALIDITY(ErrorDelegate); FSmartWalletHandle ThisCopy = *this; - UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, SuccessDelegate, ErrorDelegate] - { - TArray Admins; - if (FString Error; Thirdweb::smart_wallet_get_all_admins(ThisCopy.GetID()).AssignResult(Error)) - { - TArray> JsonValueArray; - const TSharedRef> Reader = TJsonReaderFactory<>::Create(Error); - FJsonSerializer::Deserialize(Reader, JsonValueArray); - for (int i = 0; i < JsonValueArray.Num(); i++) - { - if (JsonValueArray[i]->Type == EJson::String) - { - Admins.Emplace(JsonValueArray[i]->AsString()); - } - } - SuccessDelegate.Execute(Admins); - } - else - { - if (ErrorDelegate.IsBound()) - { - ErrorDelegate.Execute(Error); - } - } - }); + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [ThisCopy, SuccessDelegate, ErrorDelegate] + { + TArray Admins; + if (FString Error; Thirdweb::smart_wallet_get_all_admins(ThisCopy.GetID()).AssignResult(Error)) + { + TArray> JsonValueArray; + const TSharedRef> Reader = TJsonReaderFactory<>::Create(Error); + FJsonSerializer::Deserialize(Reader, JsonValueArray); + for (int i = 0; i < JsonValueArray.Num(); i++) + { + if (JsonValueArray[i]->Type == EJson::String) + { + Admins.Emplace(JsonValueArray[i]->AsString()); + } + } + SuccessDelegate.Execute(Admins); + } + else + { + if (ErrorDelegate.IsBound()) + { + ErrorDelegate.Execute(Error); + } + } + }); } void FSmartWalletHandle::AddAdmin(const FString& Signer, const FSimpleDelegate& SuccessDelegate, const FStringDelegate& ErrorDelegate) @@ -226,17 +235,18 @@ void FSmartWalletHandle::AddAdmin(const FString& Signer, const FSimpleDelegate& CHECK_VALIDITY(ErrorDelegate); FSmartWalletHandle ThisCopy = *this; - UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, Signer, SuccessDelegate, ErrorDelegate] - { - if (FString Error; Thirdweb::smart_wallet_add_admin(ThisCopy.GetID(), TO_RUST_STRING(Signer)).AssignResult(Error)) - { - SuccessDelegate.Execute(); - } - else - { - ErrorDelegate.Execute(Error); - } - }); + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [ThisCopy, Signer, SuccessDelegate, ErrorDelegate] + { + if (FString Error; Thirdweb::smart_wallet_add_admin(ThisCopy.GetID(), TO_RUST_STRING(Signer)).AssignResult(Error)) + { + SuccessDelegate.Execute(); + } + else + { + ErrorDelegate.Execute(Error); + } + }); } void FSmartWalletHandle::RemoveAdmin(const FString& Signer, const FSimpleDelegate& SuccessDelegate, const FStringDelegate& ErrorDelegate) @@ -245,17 +255,18 @@ void FSmartWalletHandle::RemoveAdmin(const FString& Signer, const FSimpleDelegat CHECK_VALIDITY(ErrorDelegate); FSmartWalletHandle ThisCopy = *this; - UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, Signer, SuccessDelegate, ErrorDelegate] - { - if (FString Error; Thirdweb::smart_wallet_remove_admin(ThisCopy.GetID(), TO_RUST_STRING(Signer)).AssignResult(Error)) - { - SuccessDelegate.Execute(); - } - else - { - ErrorDelegate.Execute(Error); - } - }); + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [ThisCopy, Signer, SuccessDelegate, ErrorDelegate] + { + if (FString Error; Thirdweb::smart_wallet_remove_admin(ThisCopy.GetID(), TO_RUST_STRING(Signer)).AssignResult(Error)) + { + SuccessDelegate.Execute(); + } + else + { + ErrorDelegate.Execute(Error); + } + }); } void FSmartWalletHandle::GetActiveSigners(const FGetActiveSignersDelegate& SuccessDelegate, const FStringDelegate& ErrorDelegate) @@ -264,26 +275,27 @@ void FSmartWalletHandle::GetActiveSigners(const FGetActiveSignersDelegate& Succe CHECK_VALIDITY(ErrorDelegate); FSmartWalletHandle ThisCopy = *this; - UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, SuccessDelegate, ErrorDelegate] - { - if (FString Error; Thirdweb::smart_wallet_get_all_active_signers(ThisCopy.GetID()).AssignResult(Error)) - { - TArray Signers; - TArray> JsonValueArray; - const TSharedRef> Reader = TJsonReaderFactory<>::Create(Error); - FJsonSerializer::Deserialize(Reader, JsonValueArray); - for (int i = 0; i < JsonValueArray.Num(); i++) - { - if (JsonValueArray[i]->Type == EJson::Object) - { - Signers.Emplace(FSigner::FromJson(JsonValueArray[i]->AsObject())); - } - } - SuccessDelegate.Execute(Signers); - } - else - { - ErrorDelegate.Execute(Error); - } - }); + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [ThisCopy, SuccessDelegate, ErrorDelegate] + { + if (FString Error; Thirdweb::smart_wallet_get_all_active_signers(ThisCopy.GetID()).AssignResult(Error)) + { + TArray Signers; + TArray> JsonValueArray; + const TSharedRef> Reader = TJsonReaderFactory<>::Create(Error); + FJsonSerializer::Deserialize(Reader, JsonValueArray); + for (int i = 0; i < JsonValueArray.Num(); i++) + { + if (JsonValueArray[i]->Type == EJson::Object) + { + Signers.Emplace(FSigner::FromJson(JsonValueArray[i]->AsObject())); + } + } + SuccessDelegate.Execute(Signers); + } + else + { + ErrorDelegate.Execute(Error); + } + }); }